diff --git a/app.json b/app.json index b7c35c5b..59b41707 100644 --- a/app.json +++ b/app.json @@ -3075,6 +3075,179 @@ } ] }, + { + "id": "motion_sensor_2_tuya_cluster", + "name": { + "en": "Motion Sensor (Tuya Cluster)" + }, + "class": "sensor", + "platforms": [ + "local" + ], + "connectivity": [ + "zigbee" + ], + "capabilities": [ + "measure_battery", + "measure_luminance", + "alarm_motion", + "alarm_battery" + ], + "energy": { + "batteries": [ + "CR2450" + ] + }, + "images": { + "large": "/drivers/motion_sensor_2_tuya_cluster/assets/images/large.png", + "small": "/drivers/motion_sensor_2_tuya_cluster/assets/images/small.png" + }, + "zigbee": { + "manufacturerName": [ + "_TZE200_3towulqd" + ], + "productId": [ + "TS0601" + ], + "endpoints": { + "1": { + "clusters": [ + 0, + 61184 + ], + "bindings": [ + 25, + 10 + ] + } + }, + "learnmode": { + "image": "/drivers/motion_sensor_2_tuya_cluster/assets/icon.svg", + "instruction": { + "en": "Use a pin to press the reset button for aprox 5 seconds until the LED blinks." + } + } + }, + "settings": [ + { + "id": "batteryThreshold", + "type": "number", + "label": { + "en": "Battery Low Alarm Voltage Threshold (%)" + }, + "hint": { + "en": "This setting determines the threshold before a battery alarm is given." + }, + "value": 20, + "attr": { + "step": 1, + "min": 1, + "max": 99 + } + }, + { + "type": "group", + "label": { + "en": "Device settings (confirm with quickly inserting pin into device)" + }, + "children": [ + { + "id": "pirSensitivity", + "type": "dropdown", + "value": "2", + "label": { + "en": "Pir Sensitivity" + }, + "values": [ + { + "id": "0", + "label": { + "en": "Low" + } + }, + { + "id": "1", + "label": { + "en": "Middle" + } + }, + { + "id": "2", + "label": { + "en": "High" + } + } + ] + }, + { + "id": "intervalTime", + "type": "dropdown", + "value": "2", + "hint": { + "en": "Version 2.0.1 and above" + }, + "label": { + "en": "Light Sense Interval Time" + }, + "values": [ + { + "id": "0", + "label": { + "en": "Low" + } + }, + { + "id": "1", + "label": { + "en": "Middle" + } + }, + { + "id": "2", + "label": { + "en": "High" + } + } + ] + }, + { + "id": "keepTime", + "type": "dropdown", + "value": "0", + "label": { + "en": "Keep time" + }, + "values": [ + { + "id": "0", + "label": { + "en": "10S" + } + }, + { + "id": "1", + "label": { + "en": "30S" + } + }, + { + "id": "2", + "label": { + "en": "60S" + } + }, + { + "id": "3", + "label": { + "en": "120S" + } + } + ] + } + ] + } + ] + }, { "id": "outdoor_2_socket", "name": { diff --git a/drivers/motion_sensor_2_tuya_cluster/assets/icon.svg b/drivers/motion_sensor_2_tuya_cluster/assets/icon.svg new file mode 100644 index 00000000..96dcb8bd --- /dev/null +++ b/drivers/motion_sensor_2_tuya_cluster/assets/icon.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/motion_sensor_2_tuya_cluster/assets/images/large.png b/drivers/motion_sensor_2_tuya_cluster/assets/images/large.png new file mode 100644 index 00000000..0e7085e6 Binary files /dev/null and b/drivers/motion_sensor_2_tuya_cluster/assets/images/large.png differ diff --git a/drivers/motion_sensor_2_tuya_cluster/assets/images/small.png b/drivers/motion_sensor_2_tuya_cluster/assets/images/small.png new file mode 100644 index 00000000..582ec9f8 Binary files /dev/null and b/drivers/motion_sensor_2_tuya_cluster/assets/images/small.png differ diff --git a/drivers/motion_sensor_2_tuya_cluster/device.js b/drivers/motion_sensor_2_tuya_cluster/device.js new file mode 100644 index 00000000..eb3c5cf5 --- /dev/null +++ b/drivers/motion_sensor_2_tuya_cluster/device.js @@ -0,0 +1,112 @@ +const TuyaSpecificCluster = require('../../lib/TuyaSpecificCluster'); +const TuyaSpecificClusterDevice = require('../../lib/TuyaSpecificClusterDevice'); +const { Cluster } = require('zigbee-clusters'); + +Cluster.addCluster(TuyaSpecificCluster); + +const dataPoints = { + pir_state: 1, + battery_percentage: 4, + interval_time: 102, + pir_sensitivity: 9, + pir_time: 10, + illuminance_value: 12 +} + +const dataTypes = { + value: 2, // [ 4 byte value ] + enum: 4, // [ 0-255 ] +}; + +const getDataValue = (dpValue) => { + switch (dpValue.datatype) { + case dataTypes.value: + return parseInt(dpValue.data.toString('hex'), 16); + case dataTypes.enum: + return dpValue.data[0]; + } +} + +class motion_sensor_2_tuya_cluster extends TuyaSpecificClusterDevice { + async onNodeInit({ zclNode }) { + this.enableDebug(); + this.printNode(); + + zclNode.endpoints[1].clusters[TuyaSpecificCluster.NAME] + .on('response', this.updateData.bind(this)) + } + + updateData(data) { + const value = getDataValue(data); + this.log(`DP: ${data.dp} - Value: ${value}`) + switch (data.dp) { + case dataPoints.pir_state: + const motion = value === 0 + this.log(`Motion status: ${motion}`); + this.setCapabilityValue('alarm_motion', motion).catch(this.error); + break; + case dataPoints.illuminance_value: + this.log(`Illuminance: ${value}`); + this.setCapabilityValue('measure_luminance', value).catch(this.error); + break; + case dataPoints.battery_percentage: + const batteryThreshold = this.getSetting('batteryThreshold') || 20; + const batteryAlarm = value < batteryThreshold + this.log(`Battery: ${value}`); + this.setCapabilityValue('measure_battery', value).catch(this.error); + this.setCapabilityValue('alarm_battery', batteryAlarm).catch(this.error); + break; + case dataPoints.pir_sensitivity: + this.log(`PIR Sensitivity: ${value}`); + break; + case dataPoints.pir_time: + this.log(`PIR Time: ${value}`); + break; + default: + this.log(`Unrecognized DP: ${data.dp}`); + } + } + + async onSettings({ newSettings, changedKeys }) { + if (changedKeys.includes('batteryThreshold')) { + const batteryThreshold = newSettings.batteryThreshold; + this.setBatteryThreshold(batteryThreshold); + } + if (changedKeys.includes('pirSensitivity')) { + const pirSensitivity = newSettings.pirSensitivity; + this.setPIRSensitivity(pirSensitivity); + } + if (changedKeys.includes('keepTime')) { + const keepTime = newSettings.keepTime; + this.setKeepTime(keepTime); + } + if (changedKeys.includes('intervalTime')) { + const intervalTime = newSettings.intervalTime; + this.setIntervalTime(intervalTime); + } + } + + async setBatteryThreshold(batteryThreshold) { + this.log(`Setting Battery Threshold: ${batteryThreshold}`); + const battery = this.getCapabilityValue('measure_battery'); + const batteryAlarm = battery < batteryThreshold; + this.setCapabilityValue('alarm_battery', batteryAlarm).catch(this.error); + } + + async setPIRSensitivity(pirSensitivity) { + this.log(`Setting PIR Sensitivity: ${pirSensitivity}`); + this.writeEnum(dataPoints.pir_sensitivity, pirSensitivity).catch(this.error); + } + + async setKeepTime(keepTime) { + this.log(`Setting Keep Time: ${keepTime}`); + this.writeEnum(dataPoints.pir_time, keepTime).catch(this.error); + } + + async setIntervalTime(intervalTime) { + this.log(`Setting Interval Time: ${intervalTime}`); + this.writeEnum(dataPoints.interval_time, intervalTime).catch(this.error); + } +} + +module.exports = motion_sensor_2_tuya_cluster; diff --git a/drivers/motion_sensor_2_tuya_cluster/driver.compose.json b/drivers/motion_sensor_2_tuya_cluster/driver.compose.json new file mode 100644 index 00000000..247c198b --- /dev/null +++ b/drivers/motion_sensor_2_tuya_cluster/driver.compose.json @@ -0,0 +1,38 @@ +{ + "id": "motion_sensor_2_tuya_cluster", + "name": { + "en": "Motion Sensor (Tuya Cluster)" + }, + "class": "sensor", + "platforms": ["local"], + "connectivity": ["zigbee"], + "capabilities": [ + "measure_battery", + "measure_luminance", + "alarm_motion", + "alarm_battery" + ], + "energy": { + "batteries": ["CR2450"] + }, + "images": { + "large": "{{driverAssetsPath}}/images/large.png", + "small": "{{driverAssetsPath}}/images/small.png" + }, + "zigbee": { + "manufacturerName": ["_TZE200_3towulqd"], + "productId": ["TS0601"], + "endpoints": { + "1": { + "clusters": [0, 61184], + "bindings": [25, 10] + } + }, + "learnmode": { + "image": "{{driverAssetsPath}}/icon.svg", + "instruction": { + "en": "Use a pin to press the reset button for aprox 5 seconds until the LED blinks." + } + } + } + } diff --git a/drivers/motion_sensor_2_tuya_cluster/driver.settings.compose.json b/drivers/motion_sensor_2_tuya_cluster/driver.settings.compose.json new file mode 100644 index 00000000..2465326c --- /dev/null +++ b/drivers/motion_sensor_2_tuya_cluster/driver.settings.compose.json @@ -0,0 +1,119 @@ +[ + { + "id": "batteryThreshold", + "type": "number", + "label": { + "en": "Battery Low Alarm Voltage Threshold (%)" + }, + "hint": { + "en": "This setting determines the threshold before a battery alarm is given." + }, + "value": 20, + "attr": { + "step": 1, + "min": 1, + "max": 99 + } + }, + { + "type": "group", + "label": { + "en": "Device settings (confirm with quickly inserting pin into device)" + }, + "children": [ + { + "id": "pirSensitivity", + "type": "dropdown", + "value": "2", + "label": { + "en": "Pir Sensitivity" + }, + "values": [ + { + "id": "0", + "label": { + "en": "Low" + } + }, + { + "id": "1", + "label": { + "en": "Middle" + } + }, + { + "id": "2", + "label": { + "en": "High" + } + } + ] + }, + { + "id": "intervalTime", + "type": "dropdown", + "value": "2", + "hint": { + "en": "Version 2.0.1 and above" + }, + "label": { + "en": "Light Sense Interval Time" + }, + "values": [ + { + "id": "0", + "label": { + "en": "Low" + } + }, + { + "id": "1", + "label": { + "en": "Middle" + } + }, + { + "id": "2", + "label": { + "en": "High" + } + } + ] + }, + { + "id": "keepTime", + "type": "dropdown", + "value": "0", + "label": { + "en": "Keep time" + }, + "values": [ + { + "id": "0", + "label": { + "en": "10S" + } + }, + { + "id": "1", + "label": { + "en": "30S" + } + }, + { + "id": "2", + "label": { + "en": "60S" + } + }, + { + "id": "3", + "label": { + "en": "120S" + } + } + ] + } + ] + } +] diff --git a/lib/TuyaSpecificClusterDevice.js b/lib/TuyaSpecificClusterDevice.js index b95a784b..4d889af1 100644 --- a/lib/TuyaSpecificClusterDevice.js +++ b/lib/TuyaSpecificClusterDevice.js @@ -99,4 +99,4 @@ class TuyaSpecificClusterDevice extends ZigBeeDevice { } -module.exports = TuyaSpecificClusterDevice; \ No newline at end of file +module.exports = TuyaSpecificClusterDevice;