Merge PR #457: Philips Hue - add support for Friends of Hue switch and Wall switch module

master
Jenkins nymea 2021-09-07 17:35:51 +02:00
commit f6c6e421ca
12 changed files with 2963 additions and 2403 deletions

View File

@ -10,14 +10,16 @@ This plugin allows to interact with the Hue bridge. Each light bulb, sensor and
* No internet or cloud connection required
* Hue Dimmer switch V1 and V2
* Hue Tap Switch
* Friends of Hue Switch (e.g. Niko, ...)
* Hue Smart Button
* Hue Wall Switch Module
* Hue Motion Sensor
* Hue Outdoor Motion Sensor
* Hue Ambient White Bulb
* Hue Color Bulb
* Hue Smart plug
* Any other Bulb that can be connected and controlled by the Hue App
* In-wall dimmers/switches that can be connected and controlled by the Hue App.
* In-wall dimmers/switches that can be connected and controlled by the Hue App
## Requirements

View File

@ -516,6 +516,21 @@ void IntegrationPluginPhilipsHue::setupThing(ThingSetupInfo *info)
return info->finish(Thing::ThingErrorNoError);
}
// Friends of Hue switch
if (thing->thingClassId() == fohThingClassId) {
HueRemote *hueFoh = new HueRemote(bridge, this);
hueFoh->setName(thing->name());
hueFoh->setId(thing->paramValue(fohThingSensorIdParamTypeId).toInt());
hueFoh->setModelId(thing->paramValue(fohThingModelIdParamTypeId).toString());
hueFoh->setUuid(thing->paramValue(fohThingUuidParamTypeId).toString());
connect(hueFoh, &HueRemote::stateChanged, this, &IntegrationPluginPhilipsHue::remoteStateChanged);
connect(hueFoh, &HueRemote::buttonPressed, this, &IntegrationPluginPhilipsHue::onRemoteButtonEvent);
m_remotes.insert(hueFoh, thing);
return info->finish(Thing::ThingErrorNoError);
}
// Hue smart button
if (thing->thingClassId() == smartButtonThingClassId) {
HueRemote *smartButton = new HueRemote(bridge, this);
@ -531,6 +546,21 @@ void IntegrationPluginPhilipsHue::setupThing(ThingSetupInfo *info)
return info->finish(Thing::ThingErrorNoError);
}
// Hue Wall Switch module
if (thing->thingClassId() == wallSwitchThingClassId) {
HueRemote *wallSwitch = new HueRemote(bridge, this);
wallSwitch->setName(thing->name());
wallSwitch->setId(thing->paramValue(wallSwitchThingSensorIdParamTypeId).toInt());
wallSwitch->setModelId(thing->paramValue(wallSwitchThingModelIdParamTypeId).toString());
wallSwitch->setUuid(thing->paramValue(wallSwitchThingUuidParamTypeId).toString());
connect(wallSwitch, &HueRemote::stateChanged, this, &IntegrationPluginPhilipsHue::remoteStateChanged);
connect(wallSwitch, &HueRemote::buttonPressed, this, &IntegrationPluginPhilipsHue::onRemoteButtonEvent);
m_remotes.insert(wallSwitch, thing);
return info->finish(Thing::ThingErrorNoError);
}
// Hue Motion sensor
if (thing->thingClassId() == motionSensorThingClassId) {
qCDebug(dcPhilipsHue) << "Setup Hue motion sensor" << thing->params();
@ -644,7 +674,7 @@ void IntegrationPluginPhilipsHue::thingRemoved(Thing *thing)
light->deleteLater();
}
if (thing->thingClassId() == remoteThingClassId || thing->thingClassId() == dimmerSwitch2ThingClassId|| thing->thingClassId() == tapThingClassId || thing->thingClassId() == smartButtonThingClassId) {
if (thing->thingClassId() == remoteThingClassId || thing->thingClassId() == dimmerSwitch2ThingClassId|| thing->thingClassId() == tapThingClassId || thing->thingClassId() == fohThingClassId || thing->thingClassId() == smartButtonThingClassId || thing->thingClassId() == wallSwitchThingClassId) {
HueRemote *remote = m_remotes.key(thing);
m_remotes.remove(remote);
remote->deleteLater();
@ -1160,10 +1190,16 @@ void IntegrationPluginPhilipsHue::remoteStateChanged()
thing->setStateValue(dimmerSwitch2BatteryCriticalStateTypeId, remote->battery() < 5);
} else if (thing->thingClassId() == tapThingClassId) {
thing->setStateValue(tapConnectedStateTypeId, remote->reachable());
} else if (thing->thingClassId() == fohThingClassId) {
thing->setStateValue(fohConnectedStateTypeId, remote->reachable());
} else if (thing->thingClassId() == smartButtonThingClassId) {
thing->setStateValue(smartButtonConnectedStateTypeId, remote->reachable());
thing->setStateValue(smartButtonBatteryLevelStateTypeId, remote->battery());
thing->setStateValue(smartButtonBatteryCriticalStateTypeId, remote->battery() < 5);
} else if (thing->thingClassId() == wallSwitchThingClassId) {
thing->setStateValue(wallSwitchConnectedStateTypeId, remote->reachable());
thing->setStateValue(wallSwitchBatteryLevelStateTypeId, remote->battery());
thing->setStateValue(wallSwitchBatteryCriticalStateTypeId, remote->battery() < 5);
}
}
@ -1223,7 +1259,7 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
param = Param(dimmerSwitch2PressedEventButtonNameParamTypeId, "POWER");
id = dimmerSwitch2PressedEventTypeId;
break;
case 1000:
case 1001:
param = Param(dimmerSwitch2LongPressedEventButtonNameParamTypeId, "POWER");
id = dimmerSwitch2LongPressedEventTypeId;
break;
@ -1231,7 +1267,7 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
param = Param(dimmerSwitch2PressedEventButtonNameParamTypeId, "DIM UP");
id = dimmerSwitch2PressedEventTypeId;
break;
case 2000:
case 2001:
param = Param(dimmerSwitch2LongPressedEventButtonNameParamTypeId, "DIM UP");
id = dimmerSwitch2LongPressedEventTypeId;
break;
@ -1239,7 +1275,7 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
param = Param(dimmerSwitch2PressedEventButtonNameParamTypeId, "DIM DOWN");
id = dimmerSwitch2PressedEventTypeId;
break;
case 3000:
case 3001:
param = Param(dimmerSwitch2LongPressedEventButtonNameParamTypeId, "DIM DOWN");
id = dimmerSwitch2LongPressedEventTypeId;
break;
@ -1247,7 +1283,7 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
param = Param(dimmerSwitch2PressedEventButtonNameParamTypeId, "HUE");
id = dimmerSwitch2PressedEventTypeId;
break;
case 4000:
case 4001:
param = Param(dimmerSwitch2LongPressedEventButtonNameParamTypeId, "HUE");
id = dimmerSwitch2LongPressedEventTypeId;
break;
@ -1256,10 +1292,10 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
return;
}
// codes ending in 2 (e.g. 1002) are short presses;
// for long presses the Dimmer Switch V2 sends 3 codes:
// for long presses the Dimmer Switch V2 sends 3 codes (same behaviour as hue remote and smart button):
// * codes ending in 0 (e.g. 1000) indicate start of long press
// * codes ending in 3 (e.g. 1003) indicate end of long press --> not yet supported by this plugin, but e.g. LongPressEnded action could be added
// * codes ending in 1 (e.g. 1001) are sent during the long press --> probably for backwards compatibility with earlier version, and therefore not added to this plugin
// * codes ending in 3 (e.g. 1003) indicate end of long press
// * codes ending in 1 (e.g. 1001) are sent during the long press
} else if (thing->thingClassId() == tapThingClassId) {
switch (buttonCode) {
case 34:
@ -1282,6 +1318,28 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
qCDebug(dcPhilipsHue()) << "Received unhandled button code from Hue Tap:" << buttonCode;
return;
}
} else if (thing->thingClassId() == fohThingClassId) {
switch (buttonCode) {
case 20:
param = Param(fohPressedEventButtonNameParamTypeId, "UPPER LEFT");
id = fohPressedEventTypeId;
break;
case 21:
param = Param(fohPressedEventButtonNameParamTypeId, "LOWER LEFT");
id = fohPressedEventTypeId;
break;
case 23:
param = Param(fohPressedEventButtonNameParamTypeId, "UPPER RIGHT");
id = fohPressedEventTypeId;
break;
case 22:
param = Param(fohPressedEventButtonNameParamTypeId, "LOWER RIGHT");
id = fohPressedEventTypeId;
break;
default:
qCDebug(dcPhilipsHue()) << "Received unhandled button code from Friends of Hue switch:" << buttonCode;
return;
}
} else if (thing->thingClassId() == smartButtonThingClassId) {
switch (buttonCode) {
case 1002:
@ -1294,6 +1352,20 @@ void IntegrationPluginPhilipsHue::onRemoteButtonEvent(int buttonCode)
qCDebug(dcPhilipsHue()) << "Received unhandled button code from Hue Smart Button:" << buttonCode;
return;
}
} else if (thing->thingClassId() == wallSwitchThingClassId) {
switch (buttonCode) {
case 1002: // temporary number, replace with code (codes for on and off?)
param = Param(wallSwitchPressedEventButtonNameParamTypeId, "ONE");
id = wallSwitchPressedEventTypeId;
break;
case 2002: // temporary number, replace with code (codes for on and off?)
param = Param(wallSwitchPressedEventButtonNameParamTypeId, "TWO");
id = wallSwitchPressedEventTypeId;
break;
default:
qCDebug(dcPhilipsHue()) << "Received unhandled button code from Hue Wall Switch Module:" << buttonCode;
return;
}
}
emitEvent(Event(id, m_remotes.value(remote)->id(), ParamList() << param));
}
@ -1648,6 +1720,30 @@ void IntegrationPluginPhilipsHue::processBridgeSensorDiscoveryResponse(Thing *th
emit autoThingsAppeared({descriptor});
qCDebug(dcPhilipsHue) << "Found new smart button" << sensorMap.value("name").toString() << model;
// Wall Switch Module
} else if (model == "RDM001") {
ThingDescriptor descriptor(wallSwitchThingClassId, sensorMap.value("name").toString(), "Philips Hue Wall Switch Module", thing->id());
ParamList params;
params.append(Param(wallSwitchThingModelIdParamTypeId, model));
params.append(Param(wallSwitchThingTypeParamTypeId, sensorMap.value("type").toString()));
params.append(Param(wallSwitchThingUuidParamTypeId, uuid));
params.append(Param(wallSwitchThingSensorIdParamTypeId, sensorId));
descriptor.setParams(params);
emit autoThingsAppeared({descriptor});
qCDebug(dcPhilipsHue) << "Found new wall switch module" << sensorMap.value("name").toString() << model;
// Friends of Hue switch
} else if (model == "FOHSWITCH") {
ThingDescriptor descriptor(fohThingClassId, sensorMap.value("name").toString(), "Friends of Hue Switch", thing->id());
ParamList params;
params.append(Param(fohThingModelIdParamTypeId, model));
params.append(Param(fohThingTypeParamTypeId, sensorMap.value("type").toString()));
params.append(Param(fohThingUuidParamTypeId, uuid));
params.append(Param(fohThingSensorIdParamTypeId, sensorId));
descriptor.setParams(params);
emit autoThingsAppeared({descriptor});
qCDebug(dcPhilipsHue) << "Found new friends of hue switch" << sensorMap.value("name").toString() << model;
// Hue Tap
} else if (sensorMap.value("type").toString() == "ZGPSwitch") {
ThingDescriptor descriptor(tapThingClassId, sensorMap.value("name").toString(), "Philips Hue Tap", thing->id());
@ -2015,8 +2111,12 @@ void IntegrationPluginPhilipsHue::bridgeReachableChanged(Thing *thing, bool reac
m_remotes.value(remote)->setStateValue(dimmerSwitch2ConnectedStateTypeId, false);
} else if (m_remotes.value(remote)->thingClassId() == tapThingClassId) {
m_remotes.value(remote)->setStateValue(tapConnectedStateTypeId, false);
} else if (m_remotes.value(remote)->thingClassId() == fohThingClassId) {
m_remotes.value(remote)->setStateValue(fohConnectedStateTypeId, false);
} else if (m_remotes.value(remote)->thingClassId() == smartButtonThingClassId) {
m_remotes.value(remote)->setStateValue(smartButtonConnectedStateTypeId, false);
} else if (m_remotes.value(remote)->thingClassId() == wallSwitchThingClassId) {
m_remotes.value(remote)->setStateValue(wallSwitchConnectedStateTypeId, false);
}
}
}
@ -2097,6 +2197,13 @@ bool IntegrationPluginPhilipsHue::sensorAlreadyAdded(const QString &uuid)
}
}
// Friends of Hue
if (thing->thingClassId() == fohThingClassId) {
if (thing->paramValue(fohThingUuidParamTypeId).toString() == uuid) {
return true;
}
}
// Hue smart button
if (thing->thingClassId() == smartButtonThingClassId) {
if (thing->paramValue(smartButtonThingUuidParamTypeId).toString() == uuid) {
@ -2104,6 +2211,13 @@ bool IntegrationPluginPhilipsHue::sensorAlreadyAdded(const QString &uuid)
}
}
// Hue wall switch module
if (thing->thingClassId() == wallSwitchThingClassId) {
if (thing->paramValue(wallSwitchThingUuidParamTypeId).toString() == uuid) {
return true;
}
}
// Outdoor sensor consits out of 3 sensors
if (thing->thingClassId() == outdoorSensorThingClassId) {
if (thing->paramValue(outdoorSensorThingSensorUuidLightParamTypeId).toString() == uuid) {

View File

@ -751,6 +751,89 @@
}
]
},
{
"id": "e967027f-f8fc-410c-8b48-6ac4c42e2777",
"name": "wallSwitch",
"displayName": "Hue Wall Switch Module",
"interfaces": ["multibutton", "wirelessconnectable"],
"createMethods": ["auto"],
"paramTypes": [
{
"id": "71c2c485-6a09-4bcd-80e5-24cdc45d323f",
"name": "modelId",
"displayName": "Model ID",
"type": "QString",
"readOnly": true
},
{
"id": "08606780-2251-4c15-bb7d-7506535e14ed",
"name": "type",
"displayName": "Type",
"type" : "QString",
"readOnly": true
},
{
"id": "4cdab881-5d2d-4443-816a-231bbb331f22",
"name": "uuid",
"displayName": "UUID",
"type" : "QString",
"readOnly": true
},
{
"id": "62a9df8c-51b5-434f-9d5d-5fa97144076a",
"name": "sensorId",
"displayName": "Sensor ID",
"type" : "int",
"readOnly": true
}
],
"stateTypes": [
{
"id": "b51071af-1290-41f1-b2eb-e84527342ade",
"name": "connected",
"displayName": "Reachable",
"displayNameEvent": "Reachable changed",
"defaultValue": false,
"type": "bool",
"cached": false
},
{
"id": "b025cab6-d128-43eb-ba63-b16861d6ab10",
"name": "batteryLevel",
"displayName": "Battery level",
"displayNameEvent": "Battery level changed",
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"minValue": 0,
"maxValue": 100
},
{
"id": "7fcf84e4-5638-46ce-9a7c-85b8bd466b38",
"name": "batteryCritical",
"displayName": "Battery critical",
"displayNameEvent": "Battery critical changed",
"type": "bool",
"defaultValue": false
}
],
"eventTypes": [
{
"id": "4623b5ff-b999-4200-ba38-89435d78fcae",
"name": "pressed",
"displayName": "Button pressed",
"paramTypes": [
{
"id": "adb4ec5e-e48f-4697-a876-e56e8458987a",
"name": "buttonName",
"displayName": "Button name",
"type": "QString",
"allowedValues": ["ONE", "TWO"]
}
]
}
]
},
{
"id": "2b8c1fb8-67ee-42e9-947b-16e0a09f0d4e",
"name": "tap",
@ -815,6 +898,70 @@
}
]
},
{
"id": "692bc4be-07b8-4b77-ab0b-a36185b17d76",
"name": "foh",
"displayName": "Friends of Hue Switch",
"interfaces": ["multibutton", "wirelessconnectable"],
"createMethods": ["auto"],
"paramTypes": [
{
"id": "664c7091-12eb-4402-8239-31da85f73d38",
"name": "modelId",
"displayName": "Model ID",
"type" : "QString",
"readOnly": true
},
{
"id": "16ca2ee1-d738-4f51-8f9a-53547d3d824e",
"name": "type",
"displayName": "Type",
"type" : "QString",
"readOnly": true
},
{
"id": "2ca66286-1caf-4e09-8e18-05bb7d7df314",
"name": "uuid",
"displayName": "UUID",
"type" : "QString",
"readOnly": true
},
{
"id": "7559d16c-b56b-42e2-8347-65582fa276c0",
"name": "sensorId",
"displayName": "Sensor ID",
"type" : "int",
"readOnly": true
}
],
"stateTypes": [
{
"id": "840b220c-656b-4f56-bbaa-ce818cffad64",
"name": "connected",
"displayName": "Reachable",
"displayNameEvent": "Reachable changed",
"defaultValue": false,
"type": "bool",
"cached": false
}
],
"eventTypes": [
{
"id": "2cc68bd3-ad73-4bf3-9905-639870d071bd",
"name": "pressed",
"displayName": "Button pressed",
"paramTypes": [
{
"id": "f1da229e-fce2-4329-8850-1c92b5bc5925",
"name": "buttonName",
"displayName": "Button name",
"type": "QString",
"allowedValues": ["UPPER LEFT", "LOWER LEFT", "UPPER RIGHT", "LOWER RIGHT"]
}
]
}
]
},
{
"id": "32dc6390-600f-4eb4-b349-cc2d6796a82a",
"name": "outdoorSensor",