From 2314335d69bab608b5e268010a121bc2ff36a5f0 Mon Sep 17 00:00:00 2001 From: Christian Fetzer Date: Mon, 23 May 2022 17:13:54 +0200 Subject: [PATCH] somfytahoma: Add support for smoke detectors --- somfytahoma/README.md | 4 +- somfytahoma/integrationpluginsomfytahoma.cpp | 62 ++++++- somfytahoma/integrationpluginsomfytahoma.json | 54 +++++++ ...be1c1-daa8-4e21-9e85-b2372ab1a450-en_US.ts | 153 ++++++++++-------- 4 files changed, 207 insertions(+), 66 deletions(-) diff --git a/somfytahoma/README.md b/somfytahoma/README.md index 7cf0d5c6..3ad0d32c 100644 --- a/somfytahoma/README.md +++ b/somfytahoma/README.md @@ -22,5 +22,5 @@ entering your personal username + password for the Somfy TaHoma API. ## Supported devices Currently this plugin supports all roller shutters, blinds, garage -door, awning drives and lights that are connectable to the TaHoma gateway. -These are Somfy iO devices as well as RTS devices. +door, awning drives, lights and smoke detectors that are connectable to the +TaHoma gateway. These are Somfy iO devices as well as RTS devices. diff --git a/somfytahoma/integrationpluginsomfytahoma.cpp b/somfytahoma/integrationpluginsomfytahoma.cpp index 6e0ad1b3..cb2bc8b3 100644 --- a/somfytahoma/integrationpluginsomfytahoma.cpp +++ b/somfytahoma/integrationpluginsomfytahoma.cpp @@ -183,6 +183,16 @@ void IntegrationPluginSomfyTahoma::setupThing(ThingSetupInfo *info) descriptor.setParams(ParamList() << Param(lightThingDeviceUrlParamTypeId, deviceUrl)); unknownDevices.append(descriptor); } + } else if (type == QStringLiteral("io:SomfySmokeIOSystemSensor")) { + Thing *thing = myThings().findByParams(ParamList() << Param(smokedetectorThingDeviceUrlParamTypeId, deviceUrl)); + if (thing) { + qCDebug(dcSomfyTahoma()) << "Found existing smoke detector:" << label << deviceUrl; + } else { + qCInfo(dcSomfyTahoma()) << "Found new smoke detector:" << label << deviceUrl; + ThingDescriptor descriptor(smokedetectorThingClassId, label, QString(), gatewayId); + descriptor.setParams(ParamList() << Param(smokedetectorThingDeviceUrlParamTypeId, deviceUrl)); + unknownDevices.append(descriptor); + } } else if (type == QStringLiteral("io:StackComponent") || type.startsWith("internal:")) { continue; @@ -201,7 +211,8 @@ void IntegrationPluginSomfyTahoma::setupThing(ThingSetupInfo *info) info->thing()->thingClassId() == venetianblindThingClassId || info->thing()->thingClassId() == garagedoorThingClassId || info->thing()->thingClassId() == awningThingClassId || - info->thing()->thingClassId() == lightThingClassId) { + info->thing()->thingClassId() == lightThingClassId || + info->thing()->thingClassId() == smokedetectorThingClassId) { info->finish(Thing::ThingErrorNoError); } } @@ -315,6 +326,10 @@ void IntegrationPluginSomfyTahoma::handleEvents(const QVariantList &events) if (thing) { device = thing->name(); } + thing = myThings().findByParams(ParamList() << Param(smokedetectorThingDeviceUrlParamTypeId, eventMap["deviceURL"])); + if (thing) { + device = thing->name(); + } qCDebug(dcSomfyTahoma()) << "Got event" << eventMap["name"].toString() << "for device" << device; qCDebug(dcSomfyTahoma()) << qUtf8Printable(QJsonDocument::fromVariant(eventVariant).toJson()); @@ -480,6 +495,47 @@ void IntegrationPluginSomfyTahoma::updateThingStates(const QString &deviceUrl, c } return; } + thing = myThings().findByParams(ParamList() << Param(smokedetectorThingDeviceUrlParamTypeId, deviceUrl)); + if (thing) { + foreach (const QVariant &stateVariant, stateList) { + QVariantMap stateMap = stateVariant.toMap(); + if (stateMap["name"] == "core:SmokeState") { + thing->setStateValue(smokedetectorFireDetectedStateTypeId, stateMap["value"] == "detected"); + } else if (stateMap["name"] == "core:MaintenanceRadioPartBatteryState") { + QString radioBattery = stateMap["value"].toString(); + pluginStorage()->beginGroup(thing->id().toString()); + pluginStorage()->setValue("radioBatteryState", stateMap["value"]); + QString sensorBattery = pluginStorage()->value("sensorBatteryState", "normal").toString(); + pluginStorage()->endGroup(); + if (radioBattery == "normal" && sensorBattery == "normal") { + thing->setStateValue(smokedetectorBatteryCriticalStateTypeId, false); + } else { + qCWarning(dcSomfyTahoma()) << "Smoke Detector" << thing->name() << " radio battery is low!"; + thing->setStateValue(smokedetectorBatteryCriticalStateTypeId, true); + } + } else if (stateMap["name"] == "core:MaintenanceSensorPartBatteryState") { + QString sensorBattery = stateMap["value"].toString(); + pluginStorage()->beginGroup(thing->id().toString()); + pluginStorage()->setValue("radioBatteryState", stateMap["value"]); + QString radioBattery = pluginStorage()->value("radioBatteryState", "normal").toString(); + pluginStorage()->endGroup(); + if (radioBattery == "normal" && sensorBattery == "normal") { + thing->setStateValue(smokedetectorBatteryCriticalStateTypeId, false); + } else { + qCWarning(dcSomfyTahoma()) << "Smoke Detector" << thing->name() << " sensor battery is low!"; + thing->setStateValue(smokedetectorBatteryCriticalStateTypeId, true); + } + } else if (stateMap["name"] == "core:StatusState") { + thing->setStateValue(smokedetectorConnectedStateTypeId, stateMap["value"] == "available"); + pluginStorage()->beginGroup(thing->id().toString()); + pluginStorage()->setValue("connected", stateMap["value"] == "available"); + pluginStorage()->endGroup(); + } else if (stateMap["name"] == "core:RSSILevelState") { + thing->setStateValue(smokedetectorSignalStrengthStateTypeId, stateMap["value"]); + } + } + return; + } } void IntegrationPluginSomfyTahoma::executeAction(ThingActionInfo *info) @@ -590,6 +646,8 @@ void IntegrationPluginSomfyTahoma::markDisconnected(Thing *thing) thing->setStateValue(awningConnectedStateTypeId, false); } else if (thing->thingClassId() == lightThingClassId) { thing->setStateValue(lightConnectedStateTypeId, false); + } else if (thing->thingClassId() == smokedetectorThingClassId) { + thing->setStateValue(smokedetectorConnectedStateTypeId, false); } foreach (Thing *child, myThings().filterByParentId(thing->id())) { markDisconnected(child); @@ -610,6 +668,8 @@ void IntegrationPluginSomfyTahoma::restoreChildConnectedState(Thing *thing) thing->setStateValue(awningConnectedStateTypeId, pluginStorage()->value("connected").toBool()); } else if (thing->thingClassId() == lightThingClassId) { thing->setStateValue(lightConnectedStateTypeId, pluginStorage()->value("connected").toBool()); + } else if (thing->thingClassId() == smokedetectorThingClassId) { + thing->setStateValue(smokedetectorConnectedStateTypeId, pluginStorage()->value("connected").toBool()); } } pluginStorage()->endGroup(); diff --git a/somfytahoma/integrationpluginsomfytahoma.json b/somfytahoma/integrationpluginsomfytahoma.json index 49ca2dea..cf3edc2a 100644 --- a/somfytahoma/integrationpluginsomfytahoma.json +++ b/somfytahoma/integrationpluginsomfytahoma.json @@ -444,7 +444,61 @@ "defaultValue": false } ] + }, + { + "id": "e883a8ef-1fb7-4d7f-b40b-9cbc0133e58a", + "name": "smokedetector", + "displayName": "Smoke Detector", + "createMethods": ["auto"], + "interfaces": ["firesensor", "battery", "wirelessconnectable"], + "paramTypes": [ + { + "id": "3a41017b-87fe-4690-b148-1f69fac67f91", + "displayName": "Device URL", + "name": "deviceUrl", + "type": "QString" + } + ], + "stateTypes": [ + { + "id": "6c3ee0ce-fc0f-477c-9a01-be61e061463b", + "name": "fireDetected", + "displayName": "Fire detected", + "displayNameEvent": "Fire detected changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "8f67b692-243b-4ce8-9983-0d1206439f16", + "name": "batteryCritical", + "displayName": "Battery critical", + "displayNameEvent": "Battery critical changed", + "type": "bool", + "defaultValue": false + }, + { + "id": "47f8506c-af7f-4928-b0ce-8c52ce4f740b", + "name": "signalStrength", + "displayName": "Signal strength", + "type": "uint", + "unit": "Percentage", + "displayNameEvent": "Signal strength changed", + "minValue": 0, + "maxValue": 100, + "defaultValue": 0 + }, + { + "id": "98cf101c-cb0e-47a9-9c27-b6a0cdf9ba4b", + "name": "connected", + "displayName": "Connected", + "type": "bool", + "displayNameEvent": "Connetion state changed", + "defaultValue": false + } + ] } + + ] } ] diff --git a/somfytahoma/translations/4e8be1c1-daa8-4e21-9e85-b2372ab1a450-en_US.ts b/somfytahoma/translations/4e8be1c1-daa8-4e21-9e85-b2372ab1a450-en_US.ts index 3a5e7e42..bb44680c 100644 --- a/somfytahoma/translations/4e8be1c1-daa8-4e21-9e85-b2372ab1a450-en_US.ts +++ b/somfytahoma/translations/4e8be1c1-daa8-4e21-9e85-b2372ab1a450-en_US.ts @@ -37,8 +37,8 @@ SomfyTahoma - - + + Angle The name of the ParamType (ThingClass: venetianblind, ActionType: angle, ID: {079c7a80-8a1c-4fd7-b40c-6800120c70fb}) ---------- @@ -46,14 +46,14 @@ The name of the StateType ({079c7a80-8a1c-4fd7-b40c-6800120c70fb}) of ThingClass - + Awning The name of the ThingClass ({d3a3bb40-4b2d-4bdc-989f-5254f03b4c90}) - - + + Brightness The name of the ParamType (ThingClass: light, ActionType: brightness, ID: {1b51ce68-1f7e-4f06-b68d-bfca2d61b353}) ---------- @@ -61,10 +61,10 @@ The name of the StateType ({1b51ce68-1f7e-4f06-b68d-bfca2d61b353}) of ThingClass - - - + + + Close The name of the ActionType ({20cae53b-f36d-425b-b937-3e46519893a3}) of ThingClass awning ---------- @@ -76,15 +76,18 @@ The name of the ActionType ({baf377c6-9fba-44cf-9f14-af0101f874b5}) of ThingClas - - - + + + + Connected - The name of the StateType ({fb8dcd84-70ad-4f3e-97c4-93296608e33d}) of ThingClass light + The name of the StateType ({98cf101c-cb0e-47a9-9c27-b6a0cdf9ba4b}) of ThingClass smokedetector +---------- +The name of the StateType ({fb8dcd84-70ad-4f3e-97c4-93296608e33d}) of ThingClass light ---------- The name of the StateType ({8f972969-10dd-4954-9c8b-de56070a6668}) of ThingClass awning ---------- @@ -100,13 +103,16 @@ The name of the StateType ({10ebf650-a93a-4ee3-945b-fba10d4e35a5}) of ThingClass - - - - + + + + + Device URL - The name of the ParamType (ThingClass: light, Type: thing, ID: {9cd2e0f2-a02f-478a-9358-6ff0f5aba9f5}) + The name of the ParamType (ThingClass: smokedetector, Type: thing, ID: {3a41017b-87fe-4690-b148-1f69fac67f91}) +---------- +The name of the ParamType (ThingClass: light, Type: thing, ID: {9cd2e0f2-a02f-478a-9358-6ff0f5aba9f5}) ---------- The name of the ParamType (ThingClass: awning, Type: thing, ID: {ca60f12e-b9da-427a-a149-195922399fd5}) ---------- @@ -118,34 +124,34 @@ The name of the ParamType (ThingClass: rollershutter, Type: thing, ID: {b3d20d6a - + Garage Door The name of the ThingClass ({cb206d74-b13c-4466-98c6-070b19ebd23a}) - + Gateway Id The name of the ParamType (ThingClass: gateway, Type: thing, ID: {e321a7d6-6dcb-4a37-baf1-c7008f2d5bdb}) - + Light The name of the ThingClass ({e569a3cc-6e79-4e24-af35-c5fa327a7314}) - + Logged in The name of the StateType ({97fefa85-db79-4efd-8d83-4a15d72996e1}) of ThingClass tahoma - - - - + + + + Moving The name of the StateType ({2507ac5a-9658-42cb-80f6-73f673c32771}) of ThingClass awning ---------- @@ -157,10 +163,10 @@ The name of the StateType ({fa9446ba-da30-4d49-8fb6-f410ecc7dba0}) of ThingClass - - - - + + + + Open The name of the ActionType ({9612954c-02cb-4159-9a29-f36eaf1c7f6a}) of ThingClass awning ---------- @@ -172,14 +178,14 @@ The name of the ActionType ({a0460180-e799-4bc6-83ba-11731ef124a3}) of ThingClas - - - - - - + + + + + + Percentage The name of the ParamType (ThingClass: awning, ActionType: percentage, ID: {c409cb9b-82ef-4f59-ae89-eb783d4ebe97}) ---------- @@ -199,8 +205,8 @@ The name of the StateType ({f954ffc7-a6aa-4d30-aee0-0484631c3344}) of ThingClass - - + + Power The name of the ParamType (ThingClass: light, ActionType: power, ID: {654ddcdf-b0b7-4c38-a70d-878f0f3857a5}) ---------- @@ -208,34 +214,46 @@ The name of the StateType ({654ddcdf-b0b7-4c38-a70d-878f0f3857a5}) of ThingClass - + Gateway pin The name of the ParamType (ThingClass: gateway, Type: thing, ID: {30b73244-e5bb-4c00-9332-702a60c03420}) - + + Battery critical + The name of the StateType ({8f67b692-243b-4ce8-9983-0d1206439f16}) of ThingClass smokedetector + + + + + Fire detected + The name of the StateType ({6c3ee0ce-fc0f-477c-9a01-be61e061463b}) of ThingClass smokedetector + + + + Roller Shutter The name of the ThingClass ({6b187fe0-a987-462d-90ac-c48efc0d0fc0}) - + Set angle The name of the ActionType ({079c7a80-8a1c-4fd7-b40c-6800120c70fb}) of ThingClass venetianblind - + Set brightness The name of the ActionType ({1b51ce68-1f7e-4f06-b68d-bfca2d61b353}) of ThingClass light - - - - + + + + Set percentage The name of the ActionType ({c409cb9b-82ef-4f59-ae89-eb783d4ebe97}) of ThingClass awning ---------- @@ -247,19 +265,22 @@ The name of the ActionType ({f954ffc7-a6aa-4d30-aee0-0484631c3344}) of ThingClas - + Set power The name of the ActionType ({654ddcdf-b0b7-4c38-a70d-878f0f3857a5}) of ThingClass light - - - - - + + + + + + Signal strength - The name of the StateType ({cfaa5533-d26e-4545-9f44-6567c9d7888a}) of ThingClass light + The name of the StateType ({47f8506c-af7f-4928-b0ce-8c52ce4f740b}) of ThingClass smokedetector +---------- +The name of the StateType ({cfaa5533-d26e-4545-9f44-6567c9d7888a}) of ThingClass light ---------- The name of the StateType ({b2ad6f4a-c507-45c3-a951-b344603cc3fc}) of ThingClass awning ---------- @@ -271,28 +292,34 @@ The name of the StateType ({67594d96-47a2-4360-a1b8-79e4f22f9ed0}) of ThingClass - + + Smoke Detector + The name of the ThingClass ({e883a8ef-1fb7-4d7f-b40b-9cbc0133e58a}) + + + + Somfy The name of the vendor ({4e42a22a-ccfb-4677-89e3-f7fa16bf6be0}) - + Somfy Tahoma The name of the plugin SomfyTahoma ({4e8be1c1-daa8-4e21-9e85-b2372ab1a450}) - + State The name of the StateType ({12af28f1-475e-4d05-9bbb-adbb86dcd69c}) of ThingClass garagedoor - - - - + + + + Stop The name of the ActionType ({33bec73b-4d15-493a-b553-bcee32c40ee1}) of ThingClass awning ---------- @@ -304,25 +331,25 @@ The name of the ActionType ({cbccf714-1188-4ac9-9c91-17fe2c99acb3}) of ThingClas - + Tahoma Account The name of the ThingClass ({fedd72b8-547d-4e4f-b73e-71344a8ba0c1}) - + Tahoma Gateway The name of the ThingClass ({6c09e0b9-f0cc-4dea-9994-9e039eff78f1}) - + User display name The name of the StateType ({75609987-be60-4932-94f6-ead791b5fa58}) of ThingClass tahoma - + Venetian Blind The name of the ThingClass ({c7160205-d864-4194-b418-060fff60f0cb})