From a59d461776a6c79fc253036905a6162257f204c9 Mon Sep 17 00:00:00 2001 From: Zhongpei Ge Date: Tue, 8 Apr 2025 14:51:35 +0800 Subject: [PATCH] generic_zigbee_sensor_improvement --- .../zigbee-sensor/fingerprints.yml | 10 +- .../profiles/generic-motion-illuminance.yml | 20 ++ .../zigbee-sensor/src/contact/init.lua | 32 +++ .../SmartThings/zigbee-sensor/src/init.lua | 65 ++--- .../src/motion-illuminance/init.lua | 33 +++ .../zigbee-sensor/src/motion/init.lua | 32 +++ .../src/test/test_zigbee_sensor.lua | 254 +++++++++++++++++- .../zigbee-sensor/src/waterleak/init.lua | 32 +++ 8 files changed, 424 insertions(+), 54 deletions(-) create mode 100755 drivers/SmartThings/zigbee-sensor/profiles/generic-motion-illuminance.yml create mode 100755 drivers/SmartThings/zigbee-sensor/src/contact/init.lua create mode 100755 drivers/SmartThings/zigbee-sensor/src/motion-illuminance/init.lua create mode 100755 drivers/SmartThings/zigbee-sensor/src/motion/init.lua create mode 100755 drivers/SmartThings/zigbee-sensor/src/waterleak/init.lua diff --git a/drivers/SmartThings/zigbee-sensor/fingerprints.yml b/drivers/SmartThings/zigbee-sensor/fingerprints.yml index e2c067ad44..014ef79844 100644 --- a/drivers/SmartThings/zigbee-sensor/fingerprints.yml +++ b/drivers/SmartThings/zigbee-sensor/fingerprints.yml @@ -5,4 +5,12 @@ zigbeeGeneric: server: - 0x0500 deviceProfileName: generic-sensor - + - id: "generic-battery-sensor" + deviceLabel: "Zigbee Generic Sensor" + deviceIdentifiers: + - 0x0402 + clusters: + server: + - 0x0500 + - 0x0001 + deviceProfileName: generic-sensor diff --git a/drivers/SmartThings/zigbee-sensor/profiles/generic-motion-illuminance.yml b/drivers/SmartThings/zigbee-sensor/profiles/generic-motion-illuminance.yml new file mode 100755 index 0000000000..af4dfab95a --- /dev/null +++ b/drivers/SmartThings/zigbee-sensor/profiles/generic-motion-illuminance.yml @@ -0,0 +1,20 @@ +name: generic-motion-illuminance +components: +- id: main + capabilities: + - id: motionSensor + version: 1 + - id: illuminanceMeasurement + version: 1 + config: + values: + - key: "illuminance.value" + range: [0, 32000] + - id: battery + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: MotionSensor \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-sensor/src/contact/init.lua b/drivers/SmartThings/zigbee-sensor/src/contact/init.lua new file mode 100755 index 0000000000..e7a4d828fa --- /dev/null +++ b/drivers/SmartThings/zigbee-sensor/src/contact/init.lua @@ -0,0 +1,32 @@ +-- Copyright 2025 SmartThings +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local defaults = require "st.zigbee.defaults" +local capabilities = require "st.capabilities" + +local is_contact_sensor = function(opts, driver, device) + if device:supports_capability(capabilities.contactSensor) then + return true + end +end + +local generic_contact_sensor = { + NAME = "Generic Contact Sensor", + supported_capabilities = { + capabilities.contactSensor + }, + can_handle = is_contact_sensor +} +defaults.register_for_default_handlers(generic_contact_sensor, generic_contact_sensor.supported_capabilities) +return generic_contact_sensor \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-sensor/src/init.lua b/drivers/SmartThings/zigbee-sensor/src/init.lua index 86d0e0ec67..85875d0f73 100644 --- a/drivers/SmartThings/zigbee-sensor/src/init.lua +++ b/drivers/SmartThings/zigbee-sensor/src/init.lua @@ -19,6 +19,7 @@ local capabilities = require "st.capabilities" local constants = require "st.zigbee.constants" local IasZoneType = require "st.zigbee.generated.types.IasZoneType" local device_management = require "st.zigbee.device_management" +local PowerConfiguration = clusters.PowerConfiguration local CONTACT_SWITCH = IasZoneType.CONTACT_SWITCH local MOTION_SENSOR = IasZoneType.MOTION_SENSOR @@ -28,6 +29,7 @@ local ZIGBEE_GENERIC_SENSOR_PROFILE = "generic-sensor" local ZIGBEE_GENERIC_CONTACT_SENSOR_PROFILE = "generic-contact-sensor" local ZIGBEE_GENERIC_MOTION_SENSOR_PROFILE = "generic-motion-sensor" local ZIGBEE_GENERIC_WATERLEAK_SENSOR_PROFILE = "generic-waterleak-sensor" +local ZIGBEE_GENERIC_MOTION_ILLUMINANCE_PROFILE = "generic-motion-illuminance" local ZONETYPE = "ZoneType" local IASZone = clusters.IASZone @@ -42,6 +44,7 @@ local do_configure = function(self, device) device:configure() device:send(device_management.build_bind_request(device, IASZone.ID, self.environment_info.hub_zigbee_eui)) device:send(IASZone.attributes.ZoneStatus:configure_reporting(device, 30, 300, 1)) + device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:read(device)) end local function info_changed(driver, device, event, args) @@ -55,12 +58,16 @@ local function update_profile(device, zone_type) local profile = ZIGBEE_GENERIC_SENSOR_PROFILE if zone_type == CONTACT_SWITCH then profile = ZIGBEE_GENERIC_CONTACT_SENSOR_PROFILE - elseif zone_type == MOTION_SENSOR then - profile = ZIGBEE_GENERIC_MOTION_SENSOR_PROFILE elseif zone_type == WATER_SENSOR then profile = ZIGBEE_GENERIC_WATERLEAK_SENSOR_PROFILE + elseif zone_type == MOTION_SENSOR then + profile = ZIGBEE_GENERIC_MOTION_SENSOR_PROFILE + for _, ep in ipairs(device.zigbee_endpoints) do + if device:supports_server_cluster(clusters.IlluminanceMeasurement.ID, ep.id) then + profile = ZIGBEE_GENERIC_MOTION_ILLUMINANCE_PROFILE + end + end end - device:try_update_metadata({profile = profile}) end @@ -70,44 +77,6 @@ local ias_zone_type_attr_handler = function (driver, device, attr_val) update_profile(device, attr_val.value) end --- since we don't have button devices using IASZone, the driver here is remaining to be updated -local generate_event_from_zone_status = function(driver, device, zone_status, zb_rx) - local type = device:get_field(ZONETYPE) - local event - if type == CONTACT_SWITCH then - if zone_status:is_alarm1_set() or zone_status:is_alarm2_set() then - event = capabilities.contactSensor.contact.open() - else - event = capabilities.contactSensor.contact.closed() - end - elseif type == MOTION_SENSOR then - if zone_status:is_alarm1_set() or zone_status:is_alarm2_set() then - event = capabilities.motionSensor.motion.active() - else - event = capabilities.motionSensor.motion.inactive() - end - elseif type == WATER_SENSOR then - if zone_status:is_alarm1_set() then - event = capabilities.waterSensor.water.wet() - else - event = capabilities.waterSensor.water.dry() - end - end - if event ~= nil then - device:emit_event_for_endpoint( - zb_rx.address_header.src_endpoint.value, - event) - end -end - -local ias_zone_status_attr_handler = function(driver, device, zone_status, zb_rx) - generate_event_from_zone_status(driver, device, zone_status, zb_rx) -end - -local ias_zone_status_change_handler = function(driver, device, zb_rx) - generate_event_from_zone_status(driver, device, zb_rx.body.zcl_body.zone_status, zb_rx) -end - local zigbee_generic_sensor_template = { supported_capabilities = { capabilities.battery, @@ -117,13 +86,7 @@ local zigbee_generic_sensor_template = { zigbee_handlers = { attr = { [IASZone.ID] = { - [IASZone.attributes.ZoneType.ID] = ias_zone_type_attr_handler, - [IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler - } - }, - cluster = { - [IASZone.ID] = { - [IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler + [IASZone.attributes.ZoneType.ID] = ias_zone_type_attr_handler } } }, @@ -132,6 +95,12 @@ local zigbee_generic_sensor_template = { doConfigure = do_configure, infoChanged = info_changed }, + sub_drivers = { + require("contact"), + require("motion"), + require("waterleak"), + require("motion-illuminance") + }, ias_zone_configuration_method = constants.IAS_ZONE_CONFIGURE_TYPE.AUTO_ENROLL_RESPONSE } diff --git a/drivers/SmartThings/zigbee-sensor/src/motion-illuminance/init.lua b/drivers/SmartThings/zigbee-sensor/src/motion-illuminance/init.lua new file mode 100755 index 0000000000..0e52ec660c --- /dev/null +++ b/drivers/SmartThings/zigbee-sensor/src/motion-illuminance/init.lua @@ -0,0 +1,33 @@ +-- Copyright 2025 SmartThings +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local defaults = require "st.zigbee.defaults" +local capabilities = require "st.capabilities" + +local is_motion_illuminance = function(opts, driver, device) + if device:supports_capability(capabilities.motionSensor) and device:supports_capability(capabilities.illuminanceMeasurement) then + return true + end +end + +local generic_motion_illuminance = { + NAME = "Generic Motion illuminance", + supported_capabilities = { + capabilities.illuminanceMeasurement, + capabilities.motionSensor + }, + can_handle = is_motion_illuminance +} +defaults.register_for_default_handlers(generic_motion_illuminance, generic_motion_illuminance.supported_capabilities) +return generic_motion_illuminance diff --git a/drivers/SmartThings/zigbee-sensor/src/motion/init.lua b/drivers/SmartThings/zigbee-sensor/src/motion/init.lua new file mode 100755 index 0000000000..e78054d645 --- /dev/null +++ b/drivers/SmartThings/zigbee-sensor/src/motion/init.lua @@ -0,0 +1,32 @@ +-- Copyright 2025 SmartThings +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local defaults = require "st.zigbee.defaults" +local capabilities = require "st.capabilities" + +local is_motion_sensor = function(opts, driver, device) + if device:supports_capability(capabilities.motionSensor) and not device:supports_capability(capabilities.illuminanceMeasurement) then + return true + end +end + +local generic_motion_sensor = { + NAME = "Generic Motion Sensor", + supported_capabilities = { + capabilities.motionSensor + }, + can_handle = is_motion_sensor +} +defaults.register_for_default_handlers(generic_motion_sensor, generic_motion_sensor.supported_capabilities) +return generic_motion_sensor diff --git a/drivers/SmartThings/zigbee-sensor/src/test/test_zigbee_sensor.lua b/drivers/SmartThings/zigbee-sensor/src/test/test_zigbee_sensor.lua index b4b06748de..5b4dd299c2 100644 --- a/drivers/SmartThings/zigbee-sensor/src/test/test_zigbee_sensor.lua +++ b/drivers/SmartThings/zigbee-sensor/src/test/test_zigbee_sensor.lua @@ -15,16 +15,17 @@ local test = require "integration_test" local zigbee_test_utils = require "integration_test.zigbee_test_utils" local clusters = require "st.zigbee.zcl.clusters" -local IASZone = clusters.IASZone local capabilities = require "st.capabilities" local t_utils = require "integration_test.utils" -local PowerConfiguration = clusters.PowerConfiguration local dkjson = require 'dkjson' local utils = require "st.utils" +local IasEnrollResponseCode = require "st.zigbee.generated.zcl_clusters.IASZone.types.EnrollResponseCode" +local IASZone = clusters.IASZone +local PowerConfiguration = clusters.PowerConfiguration +local IlluminanceMeasurement = clusters.IlluminanceMeasurement local ZoneStatusAttribute = IASZone.attributes.ZoneStatus local ZoneTypeAttribute = IASZone.attributes.ZoneType -local IasEnrollResponseCode = require "st.zigbee.generated.zcl_clusters.IASZone.types.EnrollResponseCode" local ZONETYPE = "ZoneType" local Contact_Switch = 21 -- 0x0015 @@ -34,6 +35,7 @@ local ZIGBEE_GENERIC_SENSOR_PROFILE = "generic-sensor" local ZIGBEE_GENERIC_CONTACT_SENSOR_PROFILE = "generic-contact-sensor" local ZIGBEE_GENERIC_MOTION_SENSOR_PROFILE = "generic-motion-sensor" local ZIGBEE_GENERIC_WATERLEAK_SENSOR_PROFILE = "generic-waterleak-sensor" +local ZIGBEE_GENERIC_MOTION_ILLUMINANCE_PROFILE = "generic-motion-illuminance" local mock_device_generic_sensor = test.mock_device.build_test_zigbee_device( { @@ -83,6 +85,22 @@ local mock_device_waterleak_sensor = test.mock_device.build_test_zigbee_device( } ) +local mock_device_motion_illuminance = test.mock_device.build_test_zigbee_device( + { + profile = t_utils.get_profile_definition(ZIGBEE_GENERIC_MOTION_ILLUMINANCE_PROFILE .. ".yml"), + zigbee_endpoints = { + [1] = { + id = 1, + server_clusters = { 0x0500, 0x0001 } + }, + [2] = { + id = 2, + server_clusters = { 0x0400 } + } + } + } +) + zigbee_test_utils.prepare_zigbee_env_info() local function test_init() mock_device_contact_sensor:set_field(ZONETYPE, Contact_Switch, { persist = true }) @@ -92,6 +110,7 @@ local function test_init() test.mock_device.add_test_device(mock_device_motion_sensor) test.mock_device.add_test_device(mock_device_waterleak_sensor) test.mock_device.add_test_device(mock_device_generic_sensor) + test.mock_device.add_test_device(mock_device_motion_illuminance) zigbee_test_utils.init_noop_health_check_timer() end @@ -114,6 +133,14 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Profile should change to Motion Illuminance", + function () + test.socket.zigbee:__queue_receive({mock_device_motion_illuminance.id, ZoneTypeAttribute:build_test_attr_report(mock_device_motion_illuminance, 0x000d)}) + mock_device_motion_illuminance:expect_metadata_update({profile = ZIGBEE_GENERIC_MOTION_ILLUMINANCE_PROFILE}) + end +) + test.register_coroutine_test( "Profile should change to Waterleak Sensor", function () @@ -154,6 +181,22 @@ test.register_message_test( } ) +test.register_message_test( + "Battery percentage report should be handled (motion illuminance)", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device_motion_illuminance.id, PowerConfiguration.attributes.BatteryPercentageRemaining:build_test_attr_report(mock_device_motion_illuminance, 55) } + }, + { + channel = "capability", + direction = "send", + message = mock_device_motion_illuminance:generate_test_message("main", capabilities.battery.battery(28)) + } + } +) + test.register_message_test( "Battery percentage report should be handled (water leak sensor)", { @@ -302,6 +345,89 @@ test.register_message_test( } ) +test.register_message_test( + "Reported motion illuminance should be handled: active", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device_motion_illuminance.id, ZoneStatusAttribute:build_test_attr_report(mock_device_motion_illuminance, 0x0001) } + }, + { + channel = "capability", + direction = "send", + message = mock_device_motion_illuminance:generate_test_message("main", capabilities.motionSensor.motion.active()) + } + } +) + +test.register_message_test( + "Reported motion illuminance should be handled: inactive", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device_motion_illuminance.id, ZoneStatusAttribute:build_test_attr_report(mock_device_motion_illuminance, 0x0000) } + }, + { + channel = "capability", + direction = "send", + message = mock_device_motion_illuminance:generate_test_message("main", capabilities.motionSensor.motion.inactive()) + } + } +) + +test.register_message_test( + "Illuminance report should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { + mock_device_motion_illuminance.id, + IlluminanceMeasurement.attributes.MeasuredValue:build_test_attr_report(mock_device_motion_illuminance, 21370) + } + }, + { + channel = "capability", + direction = "send", + message = mock_device_motion_illuminance:generate_test_message("main", capabilities.illuminanceMeasurement.illuminance({ value = 137 })) + } + } +) + +test.register_message_test( + "ZoneStatusChangeNotification from motion illuminance should be handled: active", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device_motion_illuminance.id, IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(mock_device_motion_illuminance, 0x0001, 0x00) } + }, + { + channel = "capability", + direction = "send", + message = mock_device_motion_illuminance:generate_test_message("main", capabilities.motionSensor.motion.active()) + } + } +) + +test.register_message_test( + "ZoneStatusChangeNotification from motion illuminance should be handled: inactive", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device_motion_illuminance.id, IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(mock_device_motion_illuminance, 0x0000, 0x00) } + }, + { + channel = "capability", + direction = "send", + message = mock_device_motion_illuminance:generate_test_message("main", capabilities.motionSensor.motion.inactive()) + } + } +) + test.register_message_test( "Reported water should be handled: wet", { @@ -412,6 +538,29 @@ test.register_coroutine_test( } ) +test.register_coroutine_test( + "Health check should check all relevant attributes(motion illuminance)", + function() + test.socket.device_lifecycle:__queue_receive({ mock_device_motion_illuminance.id, "added" }) + test.socket.zigbee:__expect_send({ + mock_device_motion_illuminance.id, + ZoneTypeAttribute:read(mock_device_motion_illuminance) + }) + test.wait_for_events() + + test.mock_time.advance_time(50000) + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.zigbee:__expect_send({ mock_device_motion_illuminance.id, PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device_motion_illuminance) }) + test.wait_for_events() + end, + { + test_init = function() + test.mock_device.add_test_device(mock_device_motion_illuminance) + test.timer.__create_and_queue_test_time_advance_timer(30, "interval", "health_check") + end + } +) + test.register_coroutine_test( "Health check should check all relevant attributes(waterleak)", function() @@ -470,6 +619,23 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Refresh necessary attributes(motion illuminance)", + function() + test.wait_for_events() + + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({ mock_device_motion_illuminance.id, { capability = "refresh", component = "main", command = "refresh", args = {} } }) + test.socket.zigbee:__expect_send( + { + mock_device_motion_illuminance.id, + PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device_motion_illuminance) + } + ) + test.socket.zigbee:__expect_send({ mock_device_motion_illuminance.id, ZoneStatusAttribute:read(mock_device_motion_illuminance) }) + end +) + test.register_coroutine_test( "Refresh necessary attributes(waterleak)", function() @@ -518,7 +684,12 @@ test.register_coroutine_test( 21600, 1) } - ) + ) + test.socket.zigbee:__expect_send( + { + mock_device_contact_sensor.id, + PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device_contact_sensor) + }) test.socket.zigbee:__expect_send({ mock_device_contact_sensor.id, IASZone.attributes.IASCIEAddress:write(mock_device_contact_sensor, zigbee_test_utils.mock_hub_eui) @@ -581,7 +752,12 @@ test.register_coroutine_test( 21600, 1) } - ) + ) + test.socket.zigbee:__expect_send( + { + mock_device_motion_sensor.id, + PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device_motion_sensor) + }) test.socket.zigbee:__expect_send({ mock_device_motion_sensor.id, IASZone.attributes.IASCIEAddress:write(mock_device_motion_sensor, zigbee_test_utils.mock_hub_eui) @@ -611,6 +787,68 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Configure should configure all necessary attributes(motion illuminance)", + function() + test.socket.device_lifecycle:__queue_receive({ mock_device_motion_illuminance.id, "added" }) + test.socket.zigbee:__expect_send({ + mock_device_motion_illuminance.id, + ZoneTypeAttribute:read(mock_device_motion_illuminance) + }) + test.wait_for_events() + + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.device_lifecycle:__queue_receive({ mock_device_motion_illuminance.id, "doConfigure" }) + test.socket.zigbee:__expect_send( + { + mock_device_motion_illuminance.id, + zigbee_test_utils.build_bind_request(mock_device_motion_illuminance, + zigbee_test_utils.mock_hub_eui, + PowerConfiguration.ID) + } + ) + test.socket.zigbee:__expect_send( + { + mock_device_motion_illuminance.id, + PowerConfiguration.attributes.BatteryPercentageRemaining:configure_reporting(mock_device_motion_illuminance, + 30, + 21600, + 1) + } + ) + test.socket.zigbee:__expect_send( + { + mock_device_motion_illuminance.id, + PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device_motion_illuminance) + }) + test.socket.zigbee:__expect_send({ + mock_device_motion_illuminance.id, + IASZone.attributes.IASCIEAddress:write(mock_device_motion_illuminance, zigbee_test_utils.mock_hub_eui) + }) + test.socket.zigbee:__expect_send({ + mock_device_motion_illuminance.id, + IASZone.server.commands.ZoneEnrollResponse(mock_device_motion_illuminance, IasEnrollResponseCode.SUCCESS, 0x00) + }) + test.socket.zigbee:__expect_send( + { + mock_device_motion_illuminance.id, + IASZone.attributes.ZoneStatus:configure_reporting(mock_device_motion_illuminance, + 30, + 300, + 0) + } + ) + test.socket.zigbee:__expect_send( + { + mock_device_motion_illuminance.id, + zigbee_test_utils.build_bind_request(mock_device_motion_illuminance, + zigbee_test_utils.mock_hub_eui, + IASZone.ID) + } + ) + mock_device_motion_illuminance:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + end +) test.register_coroutine_test( "Configure should configure all necessary attributes(waterleak)", @@ -640,6 +878,12 @@ test.register_coroutine_test( 21600, 1) } + ) + test.socket.zigbee:__expect_send( + { + mock_device_waterleak_sensor.id, + PowerConfiguration.attributes.BatteryPercentageRemaining:read(mock_device_waterleak_sensor) + } ) test.socket.zigbee:__expect_send({ mock_device_waterleak_sensor.id, diff --git a/drivers/SmartThings/zigbee-sensor/src/waterleak/init.lua b/drivers/SmartThings/zigbee-sensor/src/waterleak/init.lua new file mode 100755 index 0000000000..0f35cbf3b7 --- /dev/null +++ b/drivers/SmartThings/zigbee-sensor/src/waterleak/init.lua @@ -0,0 +1,32 @@ +-- Copyright 2025 SmartThings +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +local defaults = require "st.zigbee.defaults" +local capabilities = require "st.capabilities" + +local is_water_sensor = function(opts, driver, device) + if device:supports_capability(capabilities.waterSensor) then + return true + end +end + +local generic_water_sensor = { + NAME = "Generic Water Sensor", + supported_capabilities = { + capabilities.waterSensor + }, + can_handle = is_water_sensor +} +defaults.register_for_default_handlers(generic_water_sensor, generic_water_sensor.supported_capabilities) +return generic_water_sensor