From c39da7ea3ebd2637330cdd8d0c12d07f46bc8ce9 Mon Sep 17 00:00:00 2001 From: SwoopX Date: Mon, 23 May 2022 00:14:31 +0200 Subject: [PATCH 1/3] Add DDF for Aqara FP1 presence sensor RTCZCGQ11LM --- ...iaomi_rtczcgq11lm_fp1_presence_sensor.json | 215 ++++++++++++++++++ .../xiaomi_rtczcgq11lm_presenceevent.js | 32 +++ general.xml | 37 +++ resource.cpp | 4 + resource.h | 2 + rest_sensors.cpp | 29 ++- 6 files changed, 315 insertions(+), 4 deletions(-) create mode 100644 devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json create mode 100644 devices/xiaomi/xiaomi_rtczcgq11lm_presenceevent.js diff --git a/devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json b/devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json new file mode 100644 index 0000000000..54e769328a --- /dev/null +++ b/devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json @@ -0,0 +1,215 @@ +{ + "schema": "devcap1.schema.json", + "manufacturername": "aqara", + "modelid": "lumi.motion.ac01", + "vendor": "Xiaomi", + "product": "Aqara FP1 presence sensor RTCZCGQ11LM", + "sleeper": false, + "status": "Gold", + "subdevices": [ + { + "type": "$TYPE_PRESENCE_SENSOR", + "restapi": "/sensors", + "uuid": [ + "$address.ext", + "0x01", + "0x0406" + ], + "items": [ + { + "name": "attr/id" + }, + { + "name": "attr/lastannounced" + }, + { + "name": "attr/lastseen" + }, + { + "name": "attr/manufacturername" + }, + { + "name": "attr/modelid" + }, + { + "name": "attr/name" + }, + { + "name": "attr/swversion", + "refresh.interval": 84000, + "read": { + "at": "0x0006", + "cl": "0x0000", + "ep": 1, + "fn": "zcl" + }, + "parse": { + "at": "0x0006", + "cl": "0x0000", + "ep": 1, + "eval": "Item.val = Attr.val", + "fn": "zcl" + } + }, + { + "name": "attr/type" + }, + { + "name": "attr/uniqueid" + }, + { + "name": "config/triggerdistance", + "refresh.interval": 300, + "parse": { + "at": "0x0146", + "cl": "0xfcc0", + "ep": 1, + "eval": "if (Attr.val == 0) { Item.val = 'far' } else if (Attr.val == 1) { Item.val = 'medium' } else if (Attr.val == 2) { Item.val = 'near' }", + "fn": "zcl" + }, + "read": { + "at": "0x0146", + "cl": "0xfcc0", + "ep": 1, + "fn": "zcl" + }, + "write": { + "at": "0x0146", + "cl": "0xfcc0", + "dt": "0x20", + "ep": 1, + "eval": "if (Item.val == 'far') { 0 } else if (Item.val == 'medium') { 1 } else if (Item.val == 'near') { 2 }", + "fn": "zcl" + }, + "values": [ + ["\"far\"", ""], + ["\"medium\"", ""], + ["\"near\"", ""] + ], + "default": "far" + }, + { + "name": "config/devicemode", + "refresh.interval": 300, + "parse": { + "at": "0x0144", + "cl": "0xfcc0", + "ep": 1, + "eval": "if (Attr.val == 0) { Item.val = 'undirected' } else if (Attr.val == 1) { Item.val = 'leftright' } else { Item.val = 'unknown' }", + "fn": "zcl" + }, + "read": { + "at": "0x0144", + "cl": "0xfcc0", + "ep": 1, + "fn": "zcl" + }, + "write": { + "at": "0x0144", + "cl": "0xfcc0", + "dt": "0x20", + "ep": 1, + "eval": "if (Item.val == 'undirected') { 0 } else if (Item.val == 'leftright') { 1 }", + "fn": "zcl" + }, + "values": [ + ["\"undirected\"", "Undirected"], + ["\"leftright\"", "Left and right"] + ], + "default": "undirected" + }, + { + "name": "config/on" + }, + { + "name": "config/reachable" + }, + { + "name": "config/resetpresence", + "parse": { + "at": "0x0157", + "cl": "0xfcc0", + "ep": 1, + "eval": "Item.val = Attr.val", + "fn": "zcl" + }, + "read": { + "fn": "none" + }, + "write": { + "at": "0x0157", + "cl": "0xfcc0", + "dt": "0x20", + "ep": 1, + "eval": "Item.val", + "fn": "zcl" + }, + "default": false + }, + { + "name": "config/sensitivity", + "refresh.interval": 300, + "parse": { + "at": "0x010C", + "cl": "0xfcc0", + "ep": 1, + "eval": "Item.val = Attr.val", + "fn": "zcl" + }, + "read": { + "at": "0x010C", + "cl": "0xfcc0", + "ep": 1, + "fn": "zcl" + }, + "write": { + "at": "0x010C", + "cl": "0xfcc0", + "dt": "0x20", + "ep": 1, + "eval": "Item.val", + "fn": "zcl" + }, + "values": [ + [1, "Low"], + [2, "Medium"], + [3, "High"] + ], + "default": 3 + }, + { + "name": "state/lastupdated" + }, + { + "name": "state/presence", + "refresh.interval": 300, + "read": { + "fn": "none" + }, + "parse": { + "at": "0x0143", + "cl": "0xfcc0", + "ep": 1, + "eval": "Attr.val != 1 ? Item.val = true : Item.val = false", + "fn": "zcl" + }, + "default": false + }, + { + "name": "state/presenceevent", + "read": { + "fn": "none" + }, + "parse": { + "at": "0x0143", + "cl": "0xfcc0", + "ep": 1, + "script": "xiaomi_rtczcgq11lm_presenceevent.js", + "fn": "zcl" + }, + "default": "away" + } + ] + } + ] +} \ No newline at end of file diff --git a/devices/xiaomi/xiaomi_rtczcgq11lm_presenceevent.js b/devices/xiaomi/xiaomi_rtczcgq11lm_presenceevent.js new file mode 100644 index 0000000000..78c6cd2430 --- /dev/null +++ b/devices/xiaomi/xiaomi_rtczcgq11lm_presenceevent.js @@ -0,0 +1,32 @@ +switch (Attr.val) { + case 0: + Item.val = "enter"; + break; + case 1: + Item.val = "leave"; + break; + case 2: + Item.val = "enterleft"; + break; + case 3: + Item.val = "rightleave"; + break; + case 4: + Item.val = "enterright"; + break; + case 5: + Item.val = "leftleave"; + break; + case 6: + Item.val = "approaching"; + break; + case 7: + Item.val = "absenting"; + break; + case 8: + Item.val = "8"; + break; + case 9: + Item.val = "9"; + break; +} \ No newline at end of file diff --git a/general.xml b/general.xml index 47bca2eb07..5398f666cf 100644 --- a/general.xml +++ b/general.xml @@ -4327,6 +4327,43 @@ Contactor > On/off=0003 - HP/HC=0004 + + + Aqara specific attributes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sinope specific attributes. diff --git a/resource.cpp b/resource.cpp index 836a13f6fc..1515789c96 100644 --- a/resource.cpp +++ b/resource.cpp @@ -127,6 +127,7 @@ const char *RStateOrientationY = "state/orientation_y"; const char *RStateOrientationZ = "state/orientation_z"; const char *RStatePanel = "state/panel"; const char *RStatePresence = "state/presence"; +const char *RStatePresenceEvent = "state/presenceevent"; const char *RStatePressure = "state/pressure"; const char *RStateMoisture = "state/moisture"; const char *RStatePower = "state/power"; @@ -227,6 +228,7 @@ const char *RConfigTempMinThreshold = "config/temperatureminthreshold"; const char *RConfigHumiMaxThreshold = "config/humiditymaxthreshold"; const char *RConfigHumiMinThreshold = "config/humidityminthreshold"; const char *RConfigReachable = "config/reachable"; +const char *RConfigResetPresence = "config/resetpresence"; const char *RConfigSchedule = "config/schedule"; const char *RConfigScheduleOn = "config/schedule_on"; const char *RConfigSensitivity = "config/sensitivity"; @@ -352,6 +354,7 @@ void initResourceDescriptors() rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeInt16, QVariant::Double, RStateOrientationZ)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeString, QVariant::String, RStatePanel)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeBool, QVariant::Bool, RStatePresence)); + rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeString, QVariant::String, RStatePresenceEvent)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeInt16, QVariant::Double, RStatePressure, 0, 32767)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeInt16, QVariant::Double, RStateMoisture)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeInt16, QVariant::Double, RStatePower)); @@ -445,6 +448,7 @@ void initResourceDescriptors() rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeInt8, QVariant::Double, RConfigHumiMaxThreshold)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeInt8, QVariant::Double, RConfigHumiMinThreshold)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeBool, QVariant::Bool, RConfigReachable)); + rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeBool, QVariant::Bool, RConfigResetPresence)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeString, QVariant::String, RConfigSchedule)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeBool, QVariant::Bool, RConfigScheduleOn)); rItemDescriptors.emplace_back(ResourceItemDescriptor(DataTypeUInt8, QVariant::Double, RConfigSensitivity)); diff --git a/resource.h b/resource.h index 57efd9dc3f..a007e9d678 100644 --- a/resource.h +++ b/resource.h @@ -156,6 +156,7 @@ extern const char *RStateOrientationY; extern const char *RStateOrientationZ; extern const char *RStatePanel; extern const char *RStatePresence; +extern const char *RStatePresenceEvent; extern const char *RStatePressure; extern const char *RStateMoisture; extern const char *RStatePower; @@ -247,6 +248,7 @@ extern const char *RConfigHumiMaxThreshold; extern const char *RConfigHumiMinThreshold; extern const char *RConfigVolume; extern const char *RConfigReachable; +extern const char *RConfigResetPresence; extern const char *RConfigSchedule; extern const char *RConfigScheduleOn; extern const char *RConfigSensitivity; diff --git a/rest_sensors.cpp b/rest_sensors.cpp index 3a04426aad..8d1b803a49 100644 --- a/rest_sensors.cpp +++ b/rest_sensors.cpp @@ -857,10 +857,22 @@ int DeRestPluginPrivate::changeSensorConfig(const ApiRequest &req, ApiResponse & } else if (rid.suffix == RConfigSensitivity) // Unsigned integer { - pendingMask |= R_PENDING_SENSITIVITY; - sensor->enableRead(WRITE_SENSITIVITY); - sensor->setNextReadTime(WRITE_SENSITIVITY, QTime::currentTime()); - updated = true; + if (!devManaged) + { + pendingMask |= R_PENDING_SENSITIVITY; + sensor->enableRead(WRITE_SENSITIVITY); + sensor->setNextReadTime(WRITE_SENSITIVITY, QTime::currentTime()); + updated = true; + } + else + { + if (rsub) + { + change.addTargetValue(rid.suffix, data.uinteger); + rsub->addStateChange(change); + updated = true; + } + } } else if (rid.suffix == RConfigUsertest) // Boolean { @@ -881,6 +893,15 @@ int DeRestPluginPrivate::changeSensorConfig(const ApiRequest &req, ApiResponse & { updated = true; } + else if (rid.suffix == RConfigResetPresence) // Boolean + { + if (devManaged && rsub) + { + change.addTargetValue(rid.suffix, data.boolean); + rsub->addStateChange(change); + updated = true; + } + } else if (rid.suffix == RConfigAlert) // String { const std::array RConfigAlertValues = { { {QLatin1String("none"), 0}, {QLatin1String("select"), 2}, {QLatin1String("lselect"), 15} } }; From f92a4367065c8621dab66503fce76252729fde70 Mon Sep 17 00:00:00 2001 From: SwoopX Date: Mon, 23 May 2022 22:06:45 +0200 Subject: [PATCH 2/3] Tweak DDF and add manufacturer specific clusters --- ...iaomi_rtczcgq11lm_fp1_presence_sensor.json | 31 +++++++------------ general.xml | 4 +-- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json b/devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json index 54e769328a..fb17edeabb 100644 --- a/devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json +++ b/devices/xiaomi/xiaomi_rtczcgq11lm_fp1_presence_sensor.json @@ -36,7 +36,6 @@ }, { "name": "attr/swversion", - "refresh.interval": 84000, "read": { "at": "0x0006", "cl": "0x0000", @@ -64,7 +63,7 @@ "at": "0x0146", "cl": "0xfcc0", "ep": 1, - "eval": "if (Attr.val == 0) { Item.val = 'far' } else if (Attr.val == 1) { Item.val = 'medium' } else if (Attr.val == 2) { Item.val = 'near' }", + "eval": "if (Attr.val == 0) { Item.val = 'far' } else if (Attr.val == 1) { Item.val = 'medium' } else if (Attr.val == 2) { Item.val = 'near' } else { Item.val = 'unknown' }", "fn": "zcl" }, "read": { @@ -82,9 +81,9 @@ "fn": "zcl" }, "values": [ - ["\"far\"", ""], - ["\"medium\"", ""], - ["\"near\"", ""] + ["\"far\"", "Someone approaching is detected on high distance"], + ["\"medium\"", "Someone approaching is detected on medium distance"], + ["\"near\"", "Someone approaching is detected on low distance"] ], "default": "far" }, @@ -127,14 +126,11 @@ { "name": "config/resetpresence", "parse": { - "at": "0x0157", - "cl": "0xfcc0", + "at": "0xff07", "ep": 1, - "eval": "Item.val = Attr.val", - "fn": "zcl" - }, - "read": { - "fn": "none" + "eval": "Item.val = false", + "fn": "xiaomi:special", + "idx": "0x03" }, "write": { "at": "0x0157", @@ -143,8 +139,7 @@ "ep": 1, "eval": "Item.val", "fn": "zcl" - }, - "default": false + } }, { "name": "config/sensitivity", @@ -182,7 +177,6 @@ }, { "name": "state/presence", - "refresh.interval": 300, "read": { "fn": "none" }, @@ -190,10 +184,9 @@ "at": "0x0143", "cl": "0xfcc0", "ep": 1, - "eval": "Attr.val != 1 ? Item.val = true : Item.val = false", + "eval": "(Attr.val != 0 && Attr.val != 1 && Attr.val != 3 && Attr.val != 5) ? Item.val = true : Item.val = false", "fn": "zcl" - }, - "default": false + } }, { "name": "state/presenceevent", @@ -207,7 +200,7 @@ "script": "xiaomi_rtczcgq11lm_presenceevent.js", "fn": "zcl" }, - "default": "away" + "default": "leave" } ] } diff --git a/general.xml b/general.xml index 5398f666cf..5e08a51556 100644 --- a/general.xml +++ b/general.xml @@ -4258,7 +4258,7 @@ Contactor > On/off=0003 - HP/HC=0004 Motion sensitivity: 1 - low, 2 - medium, 3 - high - 0 - µg/m³ + °C, 1 - µg/m³ + °F, 16 - ppb + °C, 17 - ppb + °F + 0 - µg/m³ + °C, 1 - ppb + °C, 16 - µg/m³ + °F, 17 - ppb + °F @@ -4357,7 +4357,7 @@ Contactor > On/off=0003 - HP/HC=0004 - + From 9f7e46bd29a38fc8a74999a9bf5fe2e98f820342 Mon Sep 17 00:00:00 2001 From: SwoopX Date: Tue, 24 May 2022 11:14:15 +0200 Subject: [PATCH 3/3] Add missing `state/presenceevent` resource item --- devices/generic/items/state_presenceevent_item.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 devices/generic/items/state_presenceevent_item.json diff --git a/devices/generic/items/state_presenceevent_item.json b/devices/generic/items/state_presenceevent_item.json new file mode 100644 index 0000000000..12257a3a06 --- /dev/null +++ b/devices/generic/items/state_presenceevent_item.json @@ -0,0 +1,8 @@ +{ + "schema": "resourceitem1.schema.json", + "id": "state/presenceevent", + "datatype": "String", + "access": "R", + "public": true, + "description": "Describes what current activity is associated with the current presence state" +} \ No newline at end of file