diff --git a/.homeycompose/app.json b/.homeycompose/app.json index fa85c8d433..9ebad25913 100644 --- a/.homeycompose/app.json +++ b/.homeycompose/app.json @@ -28,7 +28,7 @@ "Samotech", "Silvercrest", "Smart9", - "Zemismart", + "Zemismart", "Zigbee", "Woox", "Sensor", diff --git a/app.js b/app.js index 269f2c2d67..0d8f8b9cc3 100644 --- a/app.js +++ b/app.js @@ -7,6 +7,8 @@ const { debug } = require('zigbee-clusters'); class tuyazigbee extends Homey.App { onInit() { + //require('inspector').open(9222, '0.0.0.0', true); + this.log('Tuya Zigbee app is running...'); // Register the action card for christmas lights diff --git a/app.json b/app.json index 8581cd84ac..7e277a70dd 100644 --- a/app.json +++ b/app.json @@ -10248,6 +10248,98 @@ } ] }, + { + "id": "temperaturesensor_ths317", + "name": { + "en": "Temperature Sensor THS317" + }, + "class": "sensor", + "platforms": [ + "local" + ], + "connectivity": [ + "zigbee" + ], + "capabilities": [ + "measure_temperature", + "measure_battery", + "alarm_battery" + ], + "energy": { + "batteries": [ + "AAA", + "AAA" + ] + }, + "images": { + "small": "/drivers/temperaturesensor_ths317/assets/images/small.png", + "large": "/drivers/temperaturesensor_ths317/assets/images/large.png", + "xlarge": "/drivers/temperaturesensor_ths317/assets/images/xlarge.png" + }, + "zigbee": { + "manufacturerName": [ + "_TZE200_iq4ygaai" + ], + "productId": [ + "TS0201" + ], + "endpoints": { + "1": { + "clusters": [ + 0, + 1, + 61184, + 1026 + ], + "bindings": [ + 1, + 1026, + 61184 + ] + } + }, + "learnmode": { + "image": "/drivers/temperaturesensor_ths317/assets/learnmode.svg", + "instruction": { + "en": "hold button for 10 seconds to reset the device and enter learn mode" + } + } + }, + "settings": [ + { + "id": "temperature_offset", + "type": "number", + "label": { + "en": "Temperature offset (°C)" + }, + "hint": { + "en": "Set a value to compensate a temperature offset.\nRange: -10 - 10, step size 0.1, \ndefault: 0 [°C]" + }, + "value": 0, + "attr": { + "min": -10, + "max": 10, + "step": 0.1 + } + }, + { + "id": "batteryThreshold", + "type": "number", + "label": { + "en": "Battery Low Alarm Voltage Threshold (percent)" + }, + "hint": { + "en": "This setting determines the threshold before a battery alarm is given." + }, + "value": 20, + "attr": { + "step": 1, + "min": 10, + "max": 90 + } + } + ] + }, { "id": "temphumidsensor", "name": { diff --git a/drivers/temperaturesensor_ths317/assets/icon.svg b/drivers/temperaturesensor_ths317/assets/icon.svg new file mode 100644 index 0000000000..a964c2fe27 --- /dev/null +++ b/drivers/temperaturesensor_ths317/assets/icon.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/temperaturesensor_ths317/assets/images/large.png b/drivers/temperaturesensor_ths317/assets/images/large.png new file mode 100644 index 0000000000..e5e67897c8 Binary files /dev/null and b/drivers/temperaturesensor_ths317/assets/images/large.png differ diff --git a/drivers/temperaturesensor_ths317/assets/images/small.png b/drivers/temperaturesensor_ths317/assets/images/small.png new file mode 100644 index 0000000000..e5c3cfa192 Binary files /dev/null and b/drivers/temperaturesensor_ths317/assets/images/small.png differ diff --git a/drivers/temperaturesensor_ths317/assets/images/xlarge.png b/drivers/temperaturesensor_ths317/assets/images/xlarge.png new file mode 100644 index 0000000000..7d1f7fefd8 Binary files /dev/null and b/drivers/temperaturesensor_ths317/assets/images/xlarge.png differ diff --git a/drivers/temperaturesensor_ths317/assets/learnmode.svg b/drivers/temperaturesensor_ths317/assets/learnmode.svg new file mode 100644 index 0000000000..7b4677cd9a --- /dev/null +++ b/drivers/temperaturesensor_ths317/assets/learnmode.svg @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/drivers/temperaturesensor_ths317/device.js b/drivers/temperaturesensor_ths317/device.js new file mode 100644 index 0000000000..2639bb6971 --- /dev/null +++ b/drivers/temperaturesensor_ths317/device.js @@ -0,0 +1,101 @@ +'use strict'; + +const { Cluster } = require('zigbee-clusters'); +const TuyaSpecificCluster = require('../../lib/TuyaSpecificCluster'); +const TuyaSpecificClusterDevice = require('../../lib/TuyaSpecificClusterDevice'); +const { V1_TEMPHUMID_SENSOR_DATA_POINTS } = require('../../lib/TuyaDataPoints'); +const { getDataValue } = require('../../lib/TuyaHelpers'); + +// Register Tuya cluster with Zigbee +Cluster.addCluster(TuyaSpecificCluster); + +class temperaturesensor_ths317 extends TuyaSpecificClusterDevice { + + async onNodeInit({ zclNode }) { + super.onNodeInit({ zclNode }); + + // Enable debug logging + //this.enableDebug(); + + this.printNode(); + + // Initialize reporting settings from the device settings + this.initializeReportingSettings(); + + // Listen for Tuya cluster responses + const tuyaCluster = this.zclNode.endpoints[1].clusters.tuya; + if (tuyaCluster) { + tuyaCluster.on("response", this.onTuyaClusterMessage.bind(this)); + } else { + this.error('Tuya cluster not found on endpoint 1'); + } + } + + initializeReportingSettings() { + this.temperatureOffset = this.getSetting('temperature_offset')|| 0; + this.batteryThreshold = this.getSetting('batteryThreshold')|| 20; + } + + onTuyaClusterMessage(message) { + this.log('Tuya cluster message:', message); + + try { + // Use helper function to parse datapoint value + const value = getDataValue(message); + const dp = message.dp; + + this.log(`Datapoint ${dp} report:`, value); + + // Handle different datapoints + switch (dp) { + case V1_TEMPHUMID_SENSOR_DATA_POINTS.currentTemperature: + this.onTemperatureReport(value); + break; + case V1_TEMPHUMID_SENSOR_DATA_POINTS.batteryLevel: + this.onBatteryReport(value); + break; + default: + this.log(`Unknown datapoint ${dp} report:`, value); + } + } catch (error) { + this.error('Error processing Tuya cluster report:', error); + } + } + + onTemperatureReport(measuredValue) { + const parsedValue = measuredValue / 10; + this.log('measure_temperature | temperatureMeasurement - measuredValue (temperature): ', parsedValue, ' + temperature offset ', this.temperatureOffset); + this.setCapabilityValue('measure_temperature', parsedValue + this.temperatureOffset).catch(this.error); + } + + onBatteryReport(batteryPercentageRemaining) { + this.log("measure_battery | powerConfiguration - batteryPercentageRemaining (%): ", batteryPercentageRemaining); + this.setCapabilityValue('measure_battery', batteryPercentageRemaining).catch(this.error); + this.setCapabilityValue('alarm_battery', (batteryPercentageRemaining < this.batteryThreshold) ? true : false).catch(this.error); + } + + async onSettings({oldSettings, newSettings, changedKeys}) { + // Update cached settings + if (changedKeys.includes('temperature_offset')) { + this.temperatureOffset = newSettings.temperature_offset; + this.log(`Temperature offset changed to ${this.temperatureOffset}`); + + // Update temperature capability with new offset + this.onTemperatureReport(this.getCapabilityValue('measure_temperature')*10); + } + + if (changedKeys.includes('batteryThreshold')) { + this.batteryThreshold = newSettings.batteryThreshold; + this.log(`Battery threshold changed to ${this.batteryThreshold}`); + + // Update battery alarm with new threshold + this.onBatteryReport(this.getCapabilityValue('measure_battery')); + } + } + + onDeleted() { + this.log('Temperature sensor THS317-ET-TY removed'); + } +} + +module.exports = temperaturesensor_ths317; \ No newline at end of file diff --git a/drivers/temperaturesensor_ths317/driver.compose.json b/drivers/temperaturesensor_ths317/driver.compose.json new file mode 100644 index 0000000000..01ab20a523 --- /dev/null +++ b/drivers/temperaturesensor_ths317/driver.compose.json @@ -0,0 +1,54 @@ +{ + "id": "temperaturesensor_ths317", + "name": { + "en": "Temperature Sensor THS317" + }, + "class": "sensor", + "platforms": ["local"], + "connectivity": ["zigbee"], + "capabilities": [ + "measure_temperature", + "measure_battery", + "alarm_battery" + ], + "energy": { + "batteries": [ + "AAA", + "AAA" + ] + }, + "images": { + "small": "{{driverAssetsPath}}/images/small.png", + "large": "{{driverAssetsPath}}/images/large.png", + "xlarge": "{{driverAssetsPath}}/images/xlarge.png" + }, + "zigbee": { + "manufacturerName": [ + "_TZE200_iq4ygaai" + ], + "productId": [ + "TS0201" + ], + "endpoints": { + "1": { + "clusters": [ + 0, + 1, + 61184, + 1026 + ], + "bindings": [ + 1, + 1026, + 61184 + ] + } + }, + "learnmode": { + "image": "{{driverAssetsPath}}/learnmode.svg", + "instruction": { + "en": "hold button for 10 seconds to reset the device and enter learn mode" + } + } + } +} \ No newline at end of file diff --git a/drivers/temperaturesensor_ths317/driver.settings.compose.json b/drivers/temperaturesensor_ths317/driver.settings.compose.json new file mode 100644 index 0000000000..7b1ad1093d --- /dev/null +++ b/drivers/temperaturesensor_ths317/driver.settings.compose.json @@ -0,0 +1,34 @@ + [ + { + "id": "temperature_offset", + "type": "number", + "label": { + "en": "Temperature offset (°C)" + }, + "hint": { + "en": "Set a value to compensate a temperature offset.\nRange: -10 - 10, step size 0.1, \ndefault: 0 [°C]" + }, + "value": 0, + "attr": { + "min": -10, + "max": 10, + "step": 0.1 + } + }, + { + "id": "batteryThreshold", + "type": "number", + "label": { + "en": "Battery Low Alarm Voltage Threshold (percent)" + }, + "hint": { + "en": "This setting determines the threshold before a battery alarm is given." + }, + "value": 20, + "attr": { + "step": 1, + "min": 10, + "max": 90 + } + } + ] \ No newline at end of file