Skip to content

Commit

Permalink
Support Friends of Hue switches from Vimar, Busch-Jaeger, Niko
Browse files Browse the repository at this point in the history
This is incompatible to the Hue API since instead of raw ZGP commands
the usual 1000, 1001, 1002, 1003, etc button events will be emitted.

Buttons:

1000 top left
2000 bottom left
3000 top right
4000 bottom right
5000 top left + right
6000 bottom left + right


All buttons support the events:

 * initial press
 * short release
 * hold
 * long release
  • Loading branch information
manup committed Apr 28, 2019
1 parent 80e30c5 commit 5a3767b
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 13 deletions.
111 changes: 98 additions & 13 deletions de_web_plugin.cpp
Expand Up @@ -817,17 +817,97 @@ void DeRestPluginPrivate::gpProcessButtonEvent(const deCONZ::GpDataIndication &i

*/

/*
Friends of Hue switch

A0 B0
A1 B1

DeviceId 0x02 (On/Off Switch)


A0,B0 Press 0x64 Press 2 of 2
A0,B0 Release 0x65 Release 2 of 2

A0 0x10 Press Scene0 B0 0x13 Press Scene 3
A0 0x14 Release Scene4 B0 0x17 Release Scene 7

A1 0x11 Press Scene1 B1 0x12 Press Scene 2
A1 0x15 Release Scene5 B1 0x16 Release Scene 6

A1,B1 Press 0x62 Press 1 of 2
A1,B1 Release 0x63 Release 1 of 2

*/

Sensor *sensor = getSensorNodeForAddress(ind.gpdSrcId());
ResourceItem *item = sensor ? sensor->item(RStateButtonEvent) : 0;
ResourceItem *item = sensor ? sensor->item(RStateButtonEvent) : nullptr;

if (!sensor || !item || sensor->deletedState() == Sensor::StateDeleted)
{
return;
}
quint32 btn = ind.gpdCommandId();
if (sensor->modelId() == QLatin1String("FOHSWITCH"))
{
const quint32 buttonMap[] = {
0x10, S_BUTTON_1,
0x11, S_BUTTON_2,
0x12, S_BUTTON_4,
0x13, S_BUTTON_3,
0x14, S_BUTTON_1,
0x15, S_BUTTON_2,
0x16, S_BUTTON_4,
0x17, S_BUTTON_3,
0x62, S_BUTTON_6,
0x63, S_BUTTON_6,
0x64, S_BUTTON_5,
0x65, S_BUTTON_5,
0
};

quint32 btnMapped = 0;
for (int i = 0; buttonMap[i] != 0; i += 2)
{
if (buttonMap[i] == btn)
{
btnMapped = buttonMap[i + 1];
break;
}
}

const QDateTime now = QDateTime::currentDateTime();
if (btnMapped == 0)
{
// not found
}
else if (btn == 0x10 || btn == 0x11 || btn == 0x13 || btn == 0x12 || btn == 0x64 || btn == 0x62)
{
sensor->durationDue = now.addMSecs(500); // enable generation of x001 (hold)
checkSensorsTimer->start(CHECK_SENSOR_FAST_INTERVAL);
btn = btnMapped + S_BUTTON_ACTION_INITIAL_PRESS;
}
else if (btn == 0x14 || btn == 0x15 || btn == 0x17 || btn == 0x16 || btn == 0x63 || btn == 0x65)
{
sensor->durationDue = QDateTime(); // disable generation of x001 (hold)
btn = buttonMap[btn & 0x0f];
const quint32 action = item->toNumber() & 0x03; // last action

if (action == S_BUTTON_ACTION_HOLD || // hold already triggered -> long release
item->lastSet().msecsTo(now) > 400) // over 400 ms since initial press? -> long release
{
btn = btnMapped + S_BUTTON_ACTION_LONG_RELEASED;
}
else
{
btn = btnMapped + S_BUTTON_ACTION_SHORT_RELEASED;
}
}
}

updateSensorEtag(sensor);
sensor->updateStateTimestamp();
item->setValue(ind.gpdCommandId());
item->setValue(btn);

Event e(RSensors, RStateButtonEvent, sensor->id(), item);
enqueueEvent(e);
Expand Down Expand Up @@ -923,22 +1003,22 @@ void DeRestPluginPrivate::gpDataIndication(const deCONZ::GpDataIndication &ind)
// 0x02 GPD DeviceID
// 0x81 Options (MAC Sequence number, extended options field)
// 0xF2 Extended Options Field: (Key Type: Individual, out of the box GPD key, GPD Key Present, GPD Key Encryption, GPD Outgoing present)
// 0/16 Security Key GPD Key
// 0/4 u32 GPD Key MIC
// 0/4 u32 GPD outgoing counter
// 16 Security Key GPD Key
// 4 u32 GPD Key MIC
// 4 u32 GPD outgoing counter

// Vimar (Friends of Hue) 4 button 03906
// Note 1: Niko and Busch-Jaeger Friends of Hue switches may use the same module?
// Note 2: Which modelid does Hue bridge use for Friends of Hue switches?
// Note 1: Niko and Busch-Jaeger Friends of Hue switches use the same module.
// Note 2: Hue bridge uses modelid FOHSWITCH for Friends of Hue switches.
// 0x02 GPD DeviceID
// 0xC5 Options (MAC Sequence number, application information present, extended options field)
// 0xC5 Options (MAC Sequence number, application information present, fixed location, extended options field)
// 0xF2 Extended Options Field: (Key Type: Individual, out of the box GPD key, GPD Key Present, GPD Key Encryption, GPD Outgoing present)
// 16 Security Key GPD Key
// 4 u32 GPD Key MIC
// 4 u32 GPD outgoing counter
// 0x04 ApplicationInformation (GPD commands are present)
// ManufacturerSpecific (18 byte)
// Number of GPD commands (1 byte): 0x11
// Number of GPD commands (1 byte): 0x11 (17)
// GPD CommandID list (17 byte): 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x22, 0x60, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68

// Illumra (ZBT-S1AWH and ZBT-S2AWH)
Expand Down Expand Up @@ -1018,14 +1098,13 @@ void DeRestPluginPrivate::gpDataIndication(const deCONZ::GpDataIndication &ind)
stream >> gpdOutgoingCounter;
}


SensorFingerprint fp;
fp.endpoint = GREEN_POWER_ENDPOINT;
fp.deviceId = gpdDeviceId;
fp.profileId = GP_PROFILE_ID;
fp.outClusters.push_back(GREEN_POWER_CLUSTER_ID);

Sensor *sensor = getSensorNodeForFingerPrint(ind.gpdSrcId(), fp, "ZGPSwitch");
Sensor *sensor = getSensorNodeForFingerPrint(ind.gpdSrcId(), fp, QLatin1String("ZGPSwitch"));

if (searchSensorsState == SearchSensorsActive)
{
Expand All @@ -1045,14 +1124,20 @@ void DeRestPluginPrivate::gpDataIndication(const deCONZ::GpDataIndication &ind)

// create new sensor
Sensor sensorNode;
sensorNode.setType("ZGPSwitch");

if (gpdDeviceId == deCONZ::GpDeviceIdOnOffSwitch)
if (gpdDeviceId == deCONZ::GpDeviceIdOnOffSwitch && options.byte == 0x81)
{
sensorNode.setType("ZGPSwitch");
sensorNode.setModelId("ZGPSWITCH");
sensorNode.setManufacturer("Philips");
sensorNode.setSwVersion("1.0");
}
else if (gpdDeviceId == deCONZ::GpDeviceIdOnOffSwitch && options.byte == 0xc5 && ind.payload().size() == 46)
{
sensorNode.setModelId("FOHSWITCH");
sensorNode.setManufacturer("PhilipsFoH");
sensorNode.setSwVersion("1.0");
}
else
{
DBG_Printf(DBG_INFO, "unsupported green power device 0x%02X\n", gpdDeviceId);
Expand Down
18 changes: 18 additions & 0 deletions rest_sensors.cpp
Expand Up @@ -2508,6 +2508,24 @@ void DeRestPluginPrivate::checkSensorStateTimerFired()
enqueueEvent(Event(RSensors, RStateLastUpdated, sensor->id()));
}
}
else if (sensor->modelId() == QLatin1String("FOHSWITCH"))
{
// Friends of Hue switch
// generate artificial hold event
item = sensor->item(RStateButtonEvent);
quint32 btn = item ? static_cast<quint32>(item->toNumber()) : 0;
const quint32 action = btn & 0x03;
if (btn >= S_BUTTON_1 && btn <= S_BUTTON_6 && action == S_BUTTON_ACTION_INITIAL_PRESS)
{
btn &= ~0x03;
item->setValue(btn + S_BUTTON_ACTION_HOLD);
DBG_Printf(DBG_INFO, "FoH switch button %d Hold\n", item->toNumber());
sensor->updateStateTimestamp();
Event e(RSensors, RStateButtonEvent, sensor->id(), item);
enqueueEvent(e);
enqueueEvent(Event(RSensors, RStateLastUpdated, sensor->id()));
}
}
else if (!item && sensor->modelId() == QLatin1String("lumi.vibration.aq1") && sensor->type() == QLatin1String("ZHAVibration"))
{
item = sensor->item(RStateVibration);
Expand Down

6 comments on commit 5a3767b

@ebaauw
Copy link
Collaborator

@ebaauw ebaauw commented on 5a3767b May 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incompatible to the Hue API since instead of raw ZGP commands the usual 1000, 1001, 1002, 1003, etc button events will be emitted.

While I appreciate the increased functionality, this is a pain for software like homebridge-hue that supports both deCONZ and the Hue bridge.

@manup
Copy link
Member Author

@manup manup commented on 5a3767b May 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still provide the original ZGP command values next to the derived ones.
The easiest would be just to fire multiple events, or as alternative to use two separate attributes?

@ebaauw
Copy link
Collaborator

@ebaauw ebaauw commented on 5a3767b May 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with the "usual" buttonevent values, but I do wish there's some hint in the /sensors resource as to what values to expect. Looks like the switch capabilities as exposed by the Hue API since v1.30.0 would do the trick. See #974 (comment).

@marschallphilipp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, can anybody help me with joining a friends of hue switch?

Since I don´t have a Busch-Jaeger / Vimar / Niko switch but one from Opus (https://www.opusgreen.net/produkte/einzelprodukte/opus-55-zigbee-wandsender/) I´m not sure how to join the switch to deconz within the phoscon app. In the user manual of my switch it is not mentioned how to join it to a network, it is only stated to see the manual of the gaateway :-)

As far as I understood all the switches share the same module from enocean (https://www.enocean.com/de/enocean-module-24ghz/details/ptm-215ze/), right? So joining a network should be the same for all switches!?
I saw a video of how to join a vimar switch to philips hue gateway (10s the upper left switch, then the upper right and lower left at the same time) and found the steps for the "ptm 215ze" module how to send telegrams to join a network (all steps at the same button: >7s, short press, >7s).

Both ways don´t work out for me when trying to join my switch as a generic switch within phoscon.
Any suggestions?

@manup
Copy link
Member Author

@manup manup commented on 5a3767b Jun 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is the same module you may try to join the switch with the description from Vimar:
https://download.vimar.com/irj/go/km/docs/z_catalogo/DOCUMENT/03906IEN.82882.pdf

The important part is that the initial long button press selects the channel 11, 15, 20 or 25 to operate on. Therefore the right button needs to be pressed for the actual Zigbee channel which the gateway uses.

This can also be tried by trial and error :) just press a button for 7 seconds and see if the switch search gets "green" and shows success.

@marschallphilipp
Copy link

@marschallphilipp marschallphilipp commented on 5a3767b Jun 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is the same module you may try to join the switch with the description from Vimar:
https://download.vimar.com/irj/go/km/docs/z_catalogo/DOCUMENT/03906IEN.82882.pdf

Thanks for the link @manup. Seems like the VIMAR switch uses the PTM 215Z module.
Can anybody confirm if Busch-Jaeger / Niko are using the same?

The important part is that the initial long button press selects the channel 11, 15, 20 or 25 to operate on.
Therefore the right button needs to be pressed for the actual Zigbee channel which the gateway uses.

This way of learn-in the switches is also indicating that all friends of hue switches are using the PTM 215Z module, since it corresponds with the manual of the module.
The module PTM 216Z is very similar though.....
https://www.enocean.com/de/enocean-module-24ghz/details/ptm-216z/

This can also be tried by trial and error :) just press a button for 7 seconds and see if the switch search gets "green" and shows success.

I knew that my conbee II is running on Channel 15. So I tried to switch the Channel to 15 on my Opus 55 switch and then tried to learn-in the switch again. And yeah IT WORKED.
The downside is: it was recognized as a hue tap. This is probably why I get only short press events from deconz!?

As I understood from the code within this commit the hold / long press function doesn´t come from the switch itself but from the code. Am I write? Woudl this mean it could be implemented for my switch as well?
What would you need for implementing it?
This is what I get in HA as events (Button: press / release)
A0: 34 / 35
A1: 24 / 25
B0: 20 / 21
B1: 18 / 19

Please sign in to comment.