diff --git a/drivers/SmartThings/matter-lock/fingerprints.yml b/drivers/SmartThings/matter-lock/fingerprints.yml old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml b/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml index 973c789ac6..2f4f53542f 100644 --- a/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml +++ b/drivers/SmartThings/matter-switch/profiles/ikea-scroll.yml @@ -35,4 +35,4 @@ components: - name: Button metadata: mnmn: SmartThingsEdge - vid: ikea-scroll \ No newline at end of file + vid: ikea-scroll diff --git a/drivers/SmartThings/matter-switch/src/init.lua b/drivers/SmartThings/matter-switch/src/init.lua index b1c6a8df8b..066512e529 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -36,6 +36,8 @@ function SwitchLifecycleHandlers.device_added(driver, device) -- was created after the initial subscription report if device.network_type == device_lib.NETWORK_TYPE_CHILD then device:send(clusters.OnOff.attributes.OnOff:read(device)) + elseif device.network_type == device_lib.NETWORK_TYPE_MATTER then + switch_utils.handle_electrical_sensor_info(device) end -- call device init in case init is not called after added due to device caching @@ -55,6 +57,7 @@ end function SwitchLifecycleHandlers.driver_switched(driver, device) if device.network_type == device_lib.NETWORK_TYPE_MATTER and not switch_utils.detect_bridge(device) then + switch_utils.handle_electrical_sensor_info(device) -- field settings required for proper setup when switching drivers device_cfg.match_profile(driver, device) end end diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua index 42ec81e888..8c1d7db32d 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua @@ -98,6 +98,10 @@ end test.register_coroutine_test( "Profile should not change for devices with aggregator device type (bridges)", function() + test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "added" }) + test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "init" }) + test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "doConfigure" }) + mock_bridge:expect_metadata_update({ provisioning_state = "PROVISIONED" }) end, { test_init = test_init_mock_bridge, diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua index eb38e516ee..04b574127c 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_light_fan.lua @@ -145,6 +145,12 @@ local function test_init() }) mock_device:expect_metadata_update({ profile = "fan-modular", optional_component_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}} }) mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + + local updated_device_profile = t_utils.get_profile_definition("fan-modular.yml", + {enabled_optional_capabilities = {{"main", {"fanSpeedPercent", "fanMode"}}}} + ) + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ profile = updated_device_profile })) + test.socket.matter:__expect_send({mock_device.id, subscribe_request}) end test.set_test_init_function(test_init) diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua index 189d95d2e9..12e36a6047 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua @@ -1293,4 +1293,19 @@ test.register_coroutine_test( } ) +test.register_coroutine_test( + "Refresh necessary attributes", + function() + test.socket.capability:__queue_receive( + {mock_device.id, {capability = "refresh", component = "main", command = "refresh", args = {}}} + ) + local read_request = cluster_subscribe_list[1]:read(mock_device) + for i, attr in ipairs(cluster_subscribe_list) do + if i > 1 then read_request:merge(attr:read(mock_device)) end + end + test.socket.matter:__expect_send({mock_device.id, read_request}) + test.wait_for_events() + end +) + test.run_registered_tests() diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua index b45219adca..88ae8bcaf8 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua @@ -720,6 +720,13 @@ test.register_coroutine_test( } ) +test.register_coroutine_test( + "Test init for device with requiring the switch category as a vendor override", + function() + end, + { test_init = test_init_switch_vendor_override } +) + test.register_coroutine_test( "Test init for mounted onoff control parent cluster as server", function() diff --git a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua index e9f92b6d2c..e30762cf24 100644 --- a/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua +++ b/drivers/SmartThings/matter-thermostat/src/test/test_matter_thermo_multiple_device_types.lua @@ -290,4 +290,35 @@ test.register_coroutine_test( } ) +test.register_coroutine_test( + "PercentCurrent reports and setPercent commands should be handled correctly after profile change", + function() + test_thermostat_device_type_update_modular_profile(mock_device, expected_metadata, + get_subscribe_request(mock_device, new_cluster_subscribe_list)) + + test.wait_for_events() + + test.socket.matter:__queue_receive({ + mock_device.id, + clusters.FanControl.attributes.PercentCurrent:build_test_report_data(mock_device, 2, 10) + }) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.fanSpeedPercent.percent(10)) + ) + + test.wait_for_events() + + test.socket.capability:__queue_receive({ + mock_device.id, + { capability = "fanSpeedPercent", component = "main", command = "setPercent", args = { 50 } } + }) + + test.socket.matter:__expect_send({ + mock_device.id, + clusters.FanControl.attributes.PercentSetting:write(mock_device, 2, 50) + }) + end +) + test.run_registered_tests() diff --git a/drivers/SmartThings/zigbee-bed/src/test/test_shus_mattress.lua b/drivers/SmartThings/zigbee-bed/src/test/test_shus_mattress.lua old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/zigbee-button/src/test/test_aqara_button.lua b/drivers/SmartThings/zigbee-button/src/test/test_aqara_button.lua index d15ea394b4..fcb388befb 100644 --- a/drivers/SmartThings/zigbee-button/src/test/test_aqara_button.lua +++ b/drivers/SmartThings/zigbee-button/src/test/test_aqara_button.lua @@ -36,6 +36,7 @@ local mock_device_h1_single = test.mock_device.build_test_zigbee_device( } ) +local COMP_LIST = { "button1", "button2", "all" } local mock_device_e1 = test.mock_device.build_test_zigbee_device( { profile = t_utils.get_profile_definition("one-button-batteryLevel.yml"), @@ -354,4 +355,45 @@ test.register_coroutine_test( } ) +test.register_coroutine_test( + "Wireless Remote Switch H1 Mode Change", + function() + local mode = 2 + local updates = { + preferences = { + [MODE_CHANGE] = true + } + } + test.socket.device_lifecycle:__queue_receive(mock_device_h1_double_rocker:generate_info_changed(updates)) + mock_device_h1_double_rocker:set_field("devicemode", 1, { persist = true }) + local attr_report_data = { + { PRIVATE_ATTRIBUTE_ID_ALIVE, data_types.OctetString.ID, "\x01\x21\xB8\x0B\x03\x28\x19\x04\x21\xA8\x13\x05\x21\x45\x08\x06\x24\x07\x00\x00\x00\x00\x08\x21\x15\x01\x0A\x21\xF5\x65\x0C\x20\x01\x64\x20\x01\x66\x20\x03\x67\x20\x01\x68\x21\xA8\x00" } + } + test.wait_for_events() + test.socket.zigbee:__queue_receive({ + mock_device_h1_double_rocker.id, + zigbee_test_utils.build_attribute_report(mock_device_h1_double_rocker, PRIVATE_CLUSTER_ID, attr_report_data, + MFG_CODE) + }) + test.socket.zigbee:__expect_send({ mock_device_h1_double_rocker.id, cluster_base + .write_manufacturer_specific_attribute(mock_device_h1_double_rocker, PRIVATE_CLUSTER_ID, PRIVATE_ATTRIBUTE_ID_E1, + MFG_CODE, data_types.Uint8, mode) }) + test.socket.capability:__expect_send(mock_device_h1_double_rocker:generate_test_message("main", + capabilities.button.supportedButtonValues({ "pushed", "held", "double" }, { visibility = { displayed = false } }))) + test.socket.capability:__expect_send(mock_device_h1_double_rocker:generate_test_message("main", + capabilities.button.numberOfButtons({ value = 1 }))) + test.socket.capability:__expect_send(mock_device_h1_double_rocker:generate_test_message("main", + capabilities.button.button.pushed({ state_change = false }))) + + for i = 1, 3 do + test.socket.capability:__expect_send(mock_device_h1_double_rocker:generate_test_message(COMP_LIST[i], + capabilities.button.supportedButtonValues({ "pushed", "held", "double" }, { visibility = { displayed = false } }))) + test.socket.capability:__expect_send(mock_device_h1_double_rocker:generate_test_message(COMP_LIST[i], + capabilities.button.numberOfButtons({ value = 1 }))) + test.socket.capability:__expect_send(mock_device_h1_double_rocker:generate_test_message(COMP_LIST[i], + capabilities.button.button.pushed({ state_change = false }))) + end + end +) + test.run_registered_tests() diff --git a/drivers/SmartThings/zigbee-smoke-detector/src/test/test_frient_smoke_detector.lua b/drivers/SmartThings/zigbee-smoke-detector/src/test/test_frient_smoke_detector.lua index 6ca8a3a0d9..7e87dee619 100644 --- a/drivers/SmartThings/zigbee-smoke-detector/src/test/test_frient_smoke_detector.lua +++ b/drivers/SmartThings/zigbee-smoke-detector/src/test/test_frient_smoke_detector.lua @@ -289,6 +289,24 @@ test.register_coroutine_test( } ) +test.register_coroutine_test( + "ZoneStatusChangeNotification should be handled: clear", + function() + test.timer.__create_and_queue_test_time_advance_timer(6, "oneshot") + test.socket.zigbee:__queue_receive({ + mock_device.id, + IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(mock_device, 0x0000, 0x00) + }) + + test.mock_time.advance_time(6) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.smokeDetector.smoke.clear()) + ) + + test.wait_for_events() + end +) + test.register_message_test( "Temperature report should be handled (C) for the temperature cluster", { diff --git a/drivers/SmartThings/zigbee-switch/src/aqara-light/init.lua b/drivers/SmartThings/zigbee-switch/src/aqara-light/init.lua index 2192716b21..06d4bf1cc7 100644 --- a/drivers/SmartThings/zigbee-switch/src/aqara-light/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/aqara-light/init.lua @@ -54,9 +54,18 @@ local function set_level_handler(driver, device, cmd) device:send(Level.commands.MoveToLevelWithOnOff(device, level, dimming_rate)) end +local function init(self, device) + local value = { minimum = 2700, maximum = 6000 } + if device:get_model() == "lumi.light.cwacn1" then + value.maximum = 6500 + end + emit_event_if_latest_state_missing(device, "main", capabilities.colorTemperature, capabilities.colorTemperature.colorTemperatureRange.NAME, capabilities.colorTemperature.colorTemperatureRange(value)) +end + local aqara_light_handler = { NAME = "Aqara Light Handler", lifecycle_handlers = { + init = init, added = device_added, doConfigure = do_configure }, diff --git a/drivers/SmartThings/zigbee-switch/src/init.lua b/drivers/SmartThings/zigbee-switch/src/init.lua index 062ac68782..63addc1f93 100644 --- a/drivers/SmartThings/zigbee-switch/src/init.lua +++ b/drivers/SmartThings/zigbee-switch/src/init.lua @@ -54,6 +54,8 @@ local device_init = function(driver, device) end end +local lazy_load_if_possible = require "lazy_load_subdriver" + local zigbee_switch_driver_template = { supported_capabilities = { capabilities.switch, diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_aqara_led_bulb.lua b/drivers/SmartThings/zigbee-switch/src/test/test_aqara_led_bulb.lua index d1cd3c7b72..4d18bae1fd 100644 --- a/drivers/SmartThings/zigbee-switch/src/test/test_aqara_led_bulb.lua +++ b/drivers/SmartThings/zigbee-switch/src/test/test_aqara_led_bulb.lua @@ -38,6 +38,7 @@ zigbee_test_utils.prepare_zigbee_env_info() local function test_init() test.mock_device.add_test_device(mock_device) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", capabilities.colorTemperature.colorTemperatureRange({ minimum = 2700, maximum = 6000 }))) end test.set_test_init_function(test_init) diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_aqara_light.lua b/drivers/SmartThings/zigbee-switch/src/test/test_aqara_light.lua index aeec82d57e..b05670a272 100644 --- a/drivers/SmartThings/zigbee-switch/src/test/test_aqara_light.lua +++ b/drivers/SmartThings/zigbee-switch/src/test/test_aqara_light.lua @@ -40,6 +40,7 @@ zigbee_test_utils.prepare_zigbee_env_info() local function test_init() test.mock_device.add_test_device(mock_device) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", capabilities.colorTemperature.colorTemperatureRange({ minimum = 2700, maximum = 6000 }))) end test.set_test_init_function(test_init) diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_tuya_multi.lua b/drivers/SmartThings/zigbee-switch/src/test/test_tuya_multi.lua old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/zigbee-switch/src/test/test_yanmi_switch.lua b/drivers/SmartThings/zigbee-switch/src/test/test_yanmi_switch.lua old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/zigbee-window-treatment/src/HOPOsmart/custom_clusters.lua b/drivers/SmartThings/zigbee-window-treatment/src/HOPOsmart/custom_clusters.lua old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/zigbee-window-treatment/src/HOPOsmart/init.lua b/drivers/SmartThings/zigbee-window-treatment/src/HOPOsmart/init.lua old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/zigbee-window-treatment/src/test/test_zigbee_window_shade_only_HOPOsmart.lua b/drivers/SmartThings/zigbee-window-treatment/src/test/test_zigbee_window_shade_only_HOPOsmart.lua old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/zigbee-window-treatment/src/test/test_zigbee_window_treatment_axis.lua b/drivers/SmartThings/zigbee-window-treatment/src/test/test_zigbee_window_treatment_axis.lua old mode 100755 new mode 100644 diff --git a/drivers/SmartThings/zwave-sensor/fingerprints.yml b/drivers/SmartThings/zwave-sensor/fingerprints.yml index 0810b20a38..9c0217ff39 100644 --- a/drivers/SmartThings/zwave-sensor/fingerprints.yml +++ b/drivers/SmartThings/zwave-sensor/fingerprints.yml @@ -560,6 +560,60 @@ zwaveManufacturer: productType: 0x0100 productId: 0x0082 deviceProfileName: shelly-wave-motion + - id: "aeotec/contact/8/eu" + deviceLabel: Aeotec Door Window Sensor 8 + manufacturerId: 0x0371 + productType: 0x0002 + productId: 0x0037 + deviceProfileName: aeotec-door-window-sensor-8 + - id: "aeotec/contact/8/us" + deviceLabel: Aeotec Door Window Sensor 8 + manufacturerId: 0x0371 + productType: 0x0102 + productId: 0x0037 + deviceProfileName: aeotec-door-window-sensor-8 + - id: "aeotec/contact/8/au" + deviceLabel: Aeotec Door Window Sensor 8 + manufacturerId: 0x0371 + productType: 0x0202 + productId: 0x0037 + deviceProfileName: aeotec-door-window-sensor-8 + - id: aeotec/aerq/8/eu + deviceLabel: Aeotec aerQ 8 + manufacturerId: 0x0371 + productType: 0x0002 + productId: 0x0039 + deviceProfileName: aeotec-aerq-8 + - id: aeotec/aerq/8/us + deviceLabel: Aeotec aerQ 8 + manufacturerId: 0x0371 + productType: 0x0102 + productId: 0x0039 + deviceProfileName: aeotec-aerq-8 + - id: aeotec/aerq/8/au + deviceLabel: Aeotec aerQ 8 + manufacturerId: 0x0371 + productType: 0x0202 + productId: 0x0039 + deviceProfileName: aeotec-aerq-8 + - id: "aeotec/water/8/eu" + deviceLabel: Aeotec Water Sensor 8 + manufacturerId: 0x0371 + productType: 0x0002 + productId: 0x0038 + deviceProfileName: aeotec-water-sensor-8 + - id: "aeotec/water/8/us" + deviceLabel: Aeotec Water Sensor 8 + manufacturerId: 0x0371 + productType: 0x0102 + productId: 0x0038 + deviceProfileName: aeotec-water-sensor-8 + - id: "aeotec/water/8/au" + deviceLabel: Aeotec Water Sensor 8 + manufacturerId: 0x0371 + productType: 0x0202 + productId: 0x0038 + deviceProfileName: aeotec-water-sensor-8 zwaveGeneric: - id: "GenericSensorAlarm" deviceLabel: Z-Wave Sensor diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-aerq-8.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-aerq-8.yml new file mode 100644 index 0000000000..7055dd8300 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-aerq-8.yml @@ -0,0 +1,133 @@ +name: aeotec-aerq-8 +components: +- id: main + capabilities: + - id: temperatureMeasurement + config: + values: + - key: "temperature.value" + range: [-10, 60] + version: 1 + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + version: 1 + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: TempHumiditySensor +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + desccription: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-door-window-sensor-8.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-door-window-sensor-8.yml new file mode 100644 index 0000000000..6b7206293c --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-door-window-sensor-8.yml @@ -0,0 +1,197 @@ +name: aeotec-door-window-sensor-8 +components: +- id: main + capabilities: + - id: contactSensor + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: threeAxis + version: 1 + - id: battery + version: 1 + - id: refresh + version: 1 +categories: + - name: ContactSensor +metadata: + deviceType: ContactSensor + ocfDeviceType: x.com.st.d.sensor.contact + deviceTypeId: ContactSensor +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter5" + title: "5 State when the magnet is close" + description: "This parameter allows to set the states of door/window when the magnet closes to the sensor." + required: false + preferenceType: enumeration + definition: + options: + 0: "Open=magnet far, Closed=magnet near" + 1: "Closed=magnet far, Open=magnet near" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + desccription: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter27" + title: "27 Set tilt sensor mode" + description: "Set tilt sensor mode." + required: false + preferenceType: enumeration + definition: + options: + 0: "Disable" + 1: "Enabled. Needs magnet" + 2: "Enabled. It can used without magnet." + default: 1 + - name: "parameter28" + title: "28 State of tilt in Mode 2" + description: "This parameter allows setting the state of door/window when the sensor is tilted." + required: false + preferenceType: enumeration + definition: + options: + 0: "Opened = tilted, closed = not tilted" + 1: "Closed = tilted, opened = not tilted" + default: 0 + - name: "parameter33" + title: "33 Tilt triggered angle" + description: "With this parameter, you can adjust the tilt triggered angle if the tilt is too low or too strong." + required: false + preferenceType: integer + definition: + minimum: 1 + maximum: 90 + default: 5 + - name: "parameter34" + title: "34 Timeout tilt detection Mode 1" + description: "Set the timeout of tilt detection Mode 1." + required: false + preferenceType: integer + definition: + minimum: 5 + maximum: 60 + default: 5 + - name: "parameter35" + title: "35 Timeout tilt detection Mode 2" + description: "Set the timeout of tilt detection Mode 2." + required: false + preferenceType: integer + definition: + minimum: 5 + maximum: 60 + default: 8 + - name: "parameter36" + title: "36 Min. acc. change to report" + description: "Minimum acceleration change to trigger report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-co.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-co.yml new file mode 100644 index 0000000000..c13d2254fa --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-co.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8-co +components: +- id: main + capabilities: + - id: carbonMonoxideDetector + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: AirQualityDetector +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-co2.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-co2.yml new file mode 100644 index 0000000000..36367534a6 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-co2.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8-co2 +components: +- id: main + capabilities: + - id: carbonDioxideHealthConcern + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: AirQualityDetector +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-contact.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-contact.yml new file mode 100644 index 0000000000..994cc7b4bd --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-contact.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8-contact +components: +- id: main + capabilities: + - id: contactSensor + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: ContactSensor +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-glass-break.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-glass-break.yml new file mode 100644 index 0000000000..759d0d0f94 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-glass-break.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8-glass-break +components: +- id: main + capabilities: + - id: soundDetection + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: SoundSensor +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-motion.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-motion.yml new file mode 100644 index 0000000000..576253e2cb --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-motion.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8-motion +components: +- id: main + capabilities: + - id: motionSensor + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: MotionSensor +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-panic.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-panic.yml new file mode 100644 index 0000000000..3aa63e911c --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-panic.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8-panic +components: +- id: main + capabilities: + - id: panicAlarm + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: PanicButton +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-smoke.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-smoke.yml new file mode 100644 index 0000000000..a6686b6b57 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8-smoke.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8-smoke +components: +- id: main + capabilities: + - id: smokeDetector + version: 1 + - id: temperatureMeasurement + config: + values: + - key: "temperature.value" + range: [-10, 60] + version: 1 + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: SmokeDetector +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8.yml b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8.yml new file mode 100644 index 0000000000..717b2c8ce0 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/profiles/aeotec-water-sensor-8.yml @@ -0,0 +1,162 @@ +name: aeotec-water-sensor-8 +components: +- id: main + capabilities: + - id: waterSensor + version: 1 + - id: temperatureMeasurement + version: 1 + config: + values: + - key: "temperature.value" + range: [-10, 60] + - id: relativeHumidityMeasurement + version: 1 + - id: dewPoint + version: 1 + config: + values: + - key: "dewpoint.value" + range: [-10, 60] + - id: moldHealthConcern + version: 1 + config: + values: + - key: "moldHealthConcern.value" + enabledValues: + - good + - moderate + - id: tamperAlert + version: 1 + - id: powerSource + version: 1 + config: + values: + - key: "powerSource.value" + enabledValues: + - battery + - dc + - id: battery + version: 1 + - id: refresh + version: 1 + categories: + - name: LeakSensor +preferences: + - name: "parameter1" + title: "1 Set threshold Check Time" + description: "When using battery power, follow this configuration, the minimum time is 30 seconds. When using USB power supply, for real-time detection." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 900 + - name: "parameter2" + title: "2 Min. temperature change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 20 + - name: "parameter3" + title: "3 Min. humidity change to report" + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 255 + default: 50 + - name: "parameter4" + title: "4 Enable led indication" + description: "This parameter defines when the green or red LED will indicate events. Disabling all indications may extend battery life. Off means no indications." + required: false + preferenceType: enumeration + definition: + options: + 0: "Off" + 1: "On" + default: 0 + - name: "parameter5" + title: "5 Dry contact sensor polarity" + description: "This parameter allows to set the states of alarm when the dry contact is closed" + required: false + preferenceType: enumeration + definition: + options: + 0: "Closed = Alarm, Open = Idle" + 1: "Open = Alarm, Closed = Idle" + default: 0 + - name: "parameter10" + title: "Notification Type" + description: "Set the notification type." + required: false + preferenceType: enumeration + definition: + options: + 0: "Water Alarm" + 1: "Smoke Alarm" + 2: "CO Alarm" + 3: "CO2 Alarm" + 4: "Door/Window Alarm" + 5: "Tilt Alarm" + 6: "Motion Alarm" + 7: "Glass Break Alarm" + 8: "Panic Alarm" + default: 0 + - name: "parameter13" + title: "13 Mold alarm offset" + description: "Increase the humidity threshold." + required: false + preferenceType: integer + definition: + minimum: -10 + maximum: 10 + default: 0 + - name: "parameter23" + title: "23 Low battery threshold" + description: "Report low battery report when level goes under threshold setting." + required: false + preferenceType: integer + definition: + minimum: 10 + maximum: 50 + default: 20 + - name: "parameter24" + title: "24 Periodic Reports" + description: "The period of battery, temperature and humidity report, the minimum time is 30 seconds." + required: false + preferenceType: integer + definition: + minimum: 0 + maximum: 2678400 + default: 43200 + - name: "parameter25" + title: "25 Offset value for temperature" + description: "Calibrate temperature. Scale is defined by Param 64 .eg: Value 15 means 1.5°C or 1.5F" + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter26" + title: "26 Offset value for Humidity" + description: "Calibrate humidity." + required: false + preferenceType: integer + definition: + minimum: -200 + maximum: 200 + default: 0 + - name: "parameter64" + title: "64 Temperature Scale" + description: "Scale for auto reports and settings." + required: false + preferenceType: enumeration + definition: + options: + 0: "Celsius" + 1: "Fahrenheit" + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/src/aeotec-aerq-8/init.lua b/drivers/SmartThings/zwave-sensor/src/aeotec-aerq-8/init.lua new file mode 100644 index 0000000000..b523b01c97 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/src/aeotec-aerq-8/init.lua @@ -0,0 +1,123 @@ +-- 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 capabilities = require "st.capabilities" +--- @type st.zwave.CommandClass +local cc = require "st.zwave.CommandClass" +--- @type st.zwave.CommandClass.Notification +local Notification = (require "st.zwave.CommandClass.Notification")({ version = 3 }) +--- @type st.zwave.CommandClass.Battery +local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 }) +--- @type st.zwave.CommandClass.Configuration +local Configuration = (require "st.zwave.CommandClass.Configuration")({ version = 4 }) + +local MoldHealthConcern = capabilities.moldHealthConcern +local PowerSource = capabilities.powerSource +local TamperAlert = capabilities.tamperAlert + +local AEOTEC_AERQ_FINGERPRINTS = { + { manufacturerId = 0x0371, productId = 0x0039 } -- Aeotec aerQ 8 EU/US/AU +} + +local function can_handle_aeotec_aerq(opts, driver, device, ...) + for _, fingerprint in ipairs(AEOTEC_AERQ_FINGERPRINTS) do + if device:id_match(fingerprint.manufacturerId, fingerprint.productType, fingerprint.productId) then + local subdriver = require("aeotec-aerq-8") + return true, subdriver + end + end + return false +end + +local function added_handler(driver, device) + device:send(Configuration:Get({ parameter_number = 10 })) + + device:emit_event(MoldHealthConcern.supportedMoldValues({"good", "moderate"})) + + -- Default value + device:emit_event(MoldHealthConcern.moldHealthConcern.good()) + + -- Default value + device:emit_event(PowerSource.powerSource.battery()) + + device:send(Battery:Get({})) + + device:register_native_capability_attr_handler("temperatureMeasurement", "temperature") + -- device:register_native_capability_attr_handler("colorControl", "hue") + +end + +local function do_refresh(driver, device) + device:send(Battery:Get({})) +end + +local function notification_report_handler(self, device, cmd) + local event + + -- POWER + if cmd.args.notification_type == Notification.notification_type.POWER_MANAGEMENT then + if cmd.args.event == Notification.event.power_management.AC_MAINS_DISCONNECTED then + event = capabilities.powerSource.powerSource.battery() + elseif cmd.args.event == Notification.event.power_management.AC_MAINS_RE_CONNECTED then + event = capabilities.powerSource.powerSource.dc() + elseif cmd.args.event == Notification.event.power_management.POWER_HAS_BEEN_APPLIED then + device:send(Battery:Get({})) + end + end + + -- MOLD + if cmd.args.notification_type == Notification.notification_type.WEATHER_ALARM then + if cmd.args.event == Notification.event.weather_alarm.STATE_IDLE then + event = MoldHealthConcern.moldHealthConcern.good() + elseif cmd.args.event == Notification.event.weather_alarm.MOISTURE_ALARM then + event = MoldHealthConcern.moldHealthConcern.moderate() + end + end + + -- TAMPER + if cmd.args.notification_type == Notification.notification_type.HOME_SECURITY then + if cmd.args.event == Notification.event.home_security.STATE_IDLE then + event = TamperAlert.tamper.clear() + elseif cmd.args.event == Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED then + event = TamperAlert.tamper.detected() + end + end + + if (event ~= nil) then + device:emit_event(event) + end +end + +local aeotec_aerq_8 = { + supported_capabilities = { + capabilities.powerSource + }, + zwave_handlers = { + [cc.NOTIFICATION] = { + [Notification.REPORT] = notification_report_handler + }, + }, + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh + } + }, + lifecycle_handlers = { + added = added_handler, + }, + NAME = "Aeotec aerQ 8", + can_handle = can_handle_aeotec_aerq +} + +return aeotec_aerq_8 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/src/aeotec-door-window-sensor-8/init.lua b/drivers/SmartThings/zwave-sensor/src/aeotec-door-window-sensor-8/init.lua new file mode 100644 index 0000000000..9ca600d55f --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/src/aeotec-door-window-sensor-8/init.lua @@ -0,0 +1,199 @@ +-- 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 capabilities = require "st.capabilities" +--- @type st.zwave.CommandClass +local cc = require "st.zwave.CommandClass" +--- @type st.zwave.CommandClass.Notification +local Notification = (require "st.zwave.CommandClass.Notification")({ version = 3 }) +--- @type st.zwave.CommandClass.Battery +local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 }) +--- @type st.zwave.CommandClass.SensorMultilevel +local SensorMultilevel = (require "st.zwave.CommandClass.SensorMultilevel")({ version = 11 }) +--- @type st.zwave.CommandClass.Configuration +local Configuration = (require "st.zwave.CommandClass.Configuration")({ version = 4 }) + +local MoldHealthConcern = capabilities.moldHealthConcern +local ContactSensor = capabilities.contactSensor +local PowerSource = capabilities.powerSource +local ThreeAxis = capabilities.threeAxis +local TamperAlert = capabilities.tamperAlert +local TemperatureMeasurement = capabilities.temperatureMeasurement +local RelativeHumidityMeasurement = capabilities.relativeHumidityMeasurement +local DewPoint = capabilities.dewPoint + +local AEOTEC_DOOR_WINDOW_SENSOR_8_FINGERPRINTS = { + { manufacturerId = 0x0371, productId = 0x0037 } -- Aeotec Door Window Sensor 8 EU/US/AU +} + +local function can_handle_aeotec_door_window_sensor_8(opts, driver, device, ...) + for _, fingerprint in ipairs(AEOTEC_DOOR_WINDOW_SENSOR_8_FINGERPRINTS) do + if device:id_match(fingerprint.manufacturerId, fingerprint.productType, fingerprint.productId) then + local subdriver = require("aeotec-door-window-sensor-8") + return true, subdriver + end + end + return false +end + +local function added_handler(driver, device) + device:send(Configuration:Get({ parameter_number = 10 })) + + device:emit_event(MoldHealthConcern.supportedMoldValues({"good", "moderate"})) + + -- Default value + device:emit_event(MoldHealthConcern.moldHealthConcern.good()) + + -- Default value + device:emit_event(PowerSource.powerSource.battery()) + + device:send(Battery:Get({})) +end + +local function device_init(driver, device) + device:set_field("three_axis_x", 0) + device:set_field("three_axis_y", 0) + device:set_field("three_axis_z", 0) +end + +local function do_refresh(driver, device) + device:send(Battery:Get({})) +end + +local function notification_report_handler(self, device, cmd) + local event + + -- DOOR_WINDOW + if cmd.args.notification_type == Notification.notification_type.ACCESS_CONTROL then + if cmd.args.event == Notification.event.access_control.WINDOW_DOOR_IS_CLOSED then + event = ContactSensor.contact.closed() + elseif cmd.args.event == Notification.event.access_control.WINDOW_DOOR_IS_OPEN then + event = ContactSensor.contact.open() + end + end + + -- POWER + if cmd.args.notification_type == Notification.notification_type.POWER_MANAGEMENT then + if cmd.args.event == Notification.event.power_management.AC_MAINS_DISCONNECTED then + event = PowerSource.powerSource.battery() + elseif cmd.args.event == Notification.event.power_management.AC_MAINS_RE_CONNECTED then + event = PowerSource.powerSource.dc() + elseif cmd.args.event == Notification.event.power_management.POWER_HAS_BEEN_APPLIED then + device:send(Battery:Get({})) + end + end + + -- MOLD + if cmd.args.notification_type == Notification.notification_type.WEATHER_ALARM then + if cmd.args.event == Notification.event.weather_alarm.STATE_IDLE then + event = MoldHealthConcern.moldHealthConcern.good() + elseif cmd.args.event == Notification.event.weather_alarm.MOISTURE_ALARM then + event = MoldHealthConcern.moldHealthConcern.moderate() + end + end + + -- TAMPER + if cmd.args.notification_type == Notification.notification_type.HOME_SECURITY then + if cmd.args.event == Notification.event.home_security.STATE_IDLE then + event = TamperAlert.tamper.clear() + elseif cmd.args.event == Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED then + event = TamperAlert.tamper.detected() + end + end + + if (event ~= nil) then + device:emit_event(event) + end +end + +local function sensor_multilevel_report_handler(self, device, cmd) + local event + local sensor_type = cmd.args.sensor_type + local value = cmd.args.sensor_value + + local x = device:get_field("three_axis_x") or 0 + local y = device:get_field("three_axis_y") or 0 + local z = device:get_field("three_axis_z") or 0 + + local MIN_VAL = -10000 + local MAX_VAL = 10000 + value = math.max(MIN_VAL, math.min(MAX_VAL, value)) + + if (sensor_type == SensorMultilevel.sensor_type.ACCELERATION_X_AXIS) then + x = value + device:set_field("three_axis_x", x) + event = ThreeAxis.threeAxis({value = {x, y, z}, unit = 'mG'}) + elseif (sensor_type == SensorMultilevel.sensor_type.ACCELERATION_Y_AXIS) then + y = value + device:set_field("three_axis_y", y) + event = ThreeAxis.threeAxis({value = {x, y, z}, unit = 'mG'}) + elseif (sensor_type == SensorMultilevel.sensor_type.ACCELERATION_Z_AXIS) then + z = value + device:set_field("three_axis_z", z) + event = ThreeAxis.threeAxis({value = {x, y, z}, unit = 'mG'}) + end + + if (sensor_type == SensorMultilevel.sensor_type.TEMPERATURE) then + local scale = 'C' + if (SensorMultilevel.scale.temperature.FAHRENHEIT == cmd.args.scale) then + scale = 'F' + end + event = TemperatureMeasurement.temperature({value = value, unit = scale}) + end + + if (sensor_type == SensorMultilevel.sensor_type.RELATIVE_HUMIDITY) then + event = RelativeHumidityMeasurement.humidity({value = value, unit = "%"}) + end + + if (sensor_type == SensorMultilevel.sensor_type.DEW_POINT) then + local scale = 'C' + if (SensorMultilevel.scale.dew_point.FAHRENHEIT == cmd.args.scale) then + scale = 'F' + end + event = DewPoint.dewpoint({value = value, unit = scale}) + end + + if (event ~= nil) then + device:emit_event(event) + return; + end +end + +local aeotec_door_window_sensor_8 = { + supported_capabilities = { + capabilities.powerSource, + capabilities.threeAxis, + }, + zwave_handlers = { + [cc.NOTIFICATION] = { + [Notification.REPORT] = notification_report_handler + }, + [cc.SENSOR_MULTILEVEL] = { + [SensorMultilevel.REPORT] = sensor_multilevel_report_handler + } + }, + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh + } + }, + lifecycle_handlers = { + init = device_init, + added = added_handler + }, + NAME = "Aeotec Door Window Sensor 8", + can_handle = can_handle_aeotec_door_window_sensor_8 +} + +return aeotec_door_window_sensor_8 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/src/aeotec-water-sensor-8/init.lua b/drivers/SmartThings/zwave-sensor/src/aeotec-water-sensor-8/init.lua new file mode 100644 index 0000000000..6fa0d3b637 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/src/aeotec-water-sensor-8/init.lua @@ -0,0 +1,259 @@ +-- 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 capabilities = require "st.capabilities" +--- @type st.zwave.CommandClass +local cc = require "st.zwave.CommandClass" +--- @type st.zwave.CommandClass.Notification +local Notification = (require "st.zwave.CommandClass.Notification")({ version = 3 }) +--- @type st.zwave.CommandClass.Battery +local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 }) +--- @type st.zwave.CommandClass.Configuration +local Configuration = (require "st.zwave.CommandClass.Configuration")({ version = 4 }) + +local MoldHealthConcern = capabilities.moldHealthConcern +local CarbonDioxideHealthConcern = capabilities.carbonDioxideHealthConcern +local SoundDetection = capabilities.soundDetection +local SmokeDetector = capabilities.smokeDetector +local WaterSensor = capabilities.waterSensor +local CarbonMonoxideDetector = capabilities.carbonMonoxideDetector +local TamperAlert = capabilities.tamperAlert +local MotionSensor = capabilities.motionSensor +local PowerSource = capabilities.powerSource +local ContactSensor = capabilities.contactSensor +local PanicAlarm = capabilities.panicAlarm + +local AEOTEC_WATER_SENSOR_8_FINGERPRINTS = { + { manufacturerId = 0x0371, productId = 0x0038 } -- Aeotec Water Sensor 8 EU/US/AU +} + +local DEVICE_PROFILES = { + [0] = { profile = "aeotec-water-sensor-8"}, + [1] = { profile = "aeotec-water-sensor-8-smoke"}, + [2] = { profile = "aeotec-water-sensor-8-co"}, + [3] = { profile = "aeotec-water-sensor-8-co2"}, + [4] = { profile = "aeotec-water-sensor-8-contact"}, + [5] = { profile = "aeotec-water-sensor-8-contact"}, + [6] = { profile = "aeotec-water-sensor-8-motion"}, + [7] = { profile = "aeotec-water-sensor-8-glass-break"}, + [8] = { profile = "aeotec-water-sensor-8-panic"} +} + +local function can_handle_aeotec_water_sensor_8(opts, driver, device, ...) + for _, fingerprint in ipairs(AEOTEC_WATER_SENSOR_8_FINGERPRINTS) do + if device:id_match(fingerprint.manufacturerId, fingerprint.productType, fingerprint.productId) then + local subdriver = require("aeotec-water-sensor-8") + return true, subdriver + end + end + return false +end + +local function set_profile(device, profile) + local current = device:get_field("active_profile") + if current ~= profile.profile then + device:try_update_metadata({ profile = profile.profile }) + device:set_field("active_profile", profile.profile) + + -- Set supported modes and default value based on profile + if profile.profile == "aeotec-water-sensor-8" then + device:emit_event(WaterSensor.water.dry()) + elseif profile.profile == "aeotec-water-sensor-8-glass-break" then + device:emit_event(SoundDetection.supportedSoundTypes({"noSound", "glassBreaking"})) + device:emit_event(SoundDetection.soundDetected.noSound()) + elseif profile.profile == "aeotec-water-sensor-8-co2" then + device:emit_event(CarbonDioxideHealthConcern.supportedCarbonDioxideValues({"good", "moderate"})) + device:emit_event(CarbonDioxideHealthConcern.carbonDioxideHealthConcern.good()) + elseif profile.profile == "aeotec-water-sensor-8-co" then + device:emit_event(CarbonMonoxideDetector.carbonMonoxide.clear()) + elseif profile.profile == "aeotec-water-sensor-8-contact" then + device:emit_event(ContactSensor.contact.closed()) + elseif profile.profile == "aeotec-water-sensor-8-motion" then + device:emit_event(MotionSensor.motion.inactive()) + elseif profile.profile == "aeotec-water-sensor-8-panic" then + device:emit_event(PanicAlarm.panicAlarm.clear()) + elseif profile.profile == "aeotec-water-sensor-8-smoke" then + device:emit_event(SmokeDetector.smoke.clear()) + end + end +end + +local function added_handler(driver, device) + -- Get parameter 10 to switch device profile bsaed on the parameter value + device:send(Configuration:Get({ parameter_number = 10 })) + + device:emit_event(MoldHealthConcern.supportedMoldValues({"good", "moderate"})) + -- Default value + device:emit_event(MoldHealthConcern.moldHealthConcern.good()) + + -- Default value + device:emit_event(PowerSource.powerSource.battery()) + + device:send(Battery:Get({})) +end + +local function do_refresh(driver, device) + device:send(Battery:Get({})) +end + +local function notification_report_handler(self, device, cmd) + local active_profile = device:get_field("active_profile") + local event + local event_parameter + + if (0 ~= string.len(cmd.args.event_parameter)) then + event_parameter = string.byte(cmd.args.event_parameter) + end + + -- MOTION, GLASS_BREAK, TAMPER + if cmd.args.notification_type == Notification.notification_type.HOME_SECURITY then + -- TAMPER + if cmd.args.event == Notification.event.home_security.STATE_IDLE and event_parameter == Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED then + event = TamperAlert.tamper.clear() + elseif cmd.args.event == Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED and event_parameter == Notification.event.home_security.STATE_IDLE then + event = TamperAlert.tamper.detected() + elseif active_profile == 'aeotec-water-sensor-8-motion' then -- MOTION + if cmd.args.event == Notification.event.home_security.STATE_IDLE and event_parameter == Notification.event.home_security.MOTION_DETECTION then + event = MotionSensor.motion.inactive() + elseif cmd.args.event == Notification.event.home_security.MOTION_DETECTION then + event = MotionSensor.motion.active() + end + elseif active_profile == 'aeotec-water-sensor-8-glass-break' then -- GLASS_BREAK + if cmd.args.event == Notification.event.home_security.STATE_IDLE and event_parameter == Notification.event.home_security.GLASS_BREAKAGE then + event = SoundDetection.soundDetected.noSound() + elseif cmd.args.event == Notification.event.home_security.GLASS_BREAKAGE then + event = SoundDetection.soundDetected.glassBreaking() + end + end + end + + if cmd.args.notification_type == Notification.notification_type.POWER_MANAGEMENT then + if cmd.args.event == Notification.event.power_management.AC_MAINS_DISCONNECTED then + event = PowerSource.powerSource.battery() + elseif cmd.args.event == Notification.event.power_management.AC_MAINS_RE_CONNECTED then + event = PowerSource.powerSource.dc() + elseif cmd.args.event == Notification.event.power_management.POWER_HAS_BEEN_APPLIED then + device:send(Battery:Get({})) + end + end + + -- WATER + if cmd.args.notification_type == Notification.notification_type.WATER then + if cmd.args.event == Notification.event.water.STATE_IDLE then + event = WaterSensor.water.dry() + elseif cmd.args.event == Notification.event.water.LEAK_DETECTED then + event = WaterSensor.water.wet() + end + end + + -- MOLD + if cmd.args.notification_type == Notification.notification_type.WEATHER_ALARM then + if cmd.args.event == Notification.event.weather_alarm.STATE_IDLE then + event = MoldHealthConcern.moldHealthConcern.good() + elseif cmd.args.event == Notification.event.weather_alarm.MOISTURE_ALARM then + event = MoldHealthConcern.moldHealthConcern.moderate() + end + end + + -- SMOKE + if cmd.args.notification_type == Notification.notification_type.SMOKE then + if cmd.args.event == Notification.event.smoke.STATE_IDLE then + event = SmokeDetector.smoke.clear() + elseif cmd.args.event == Notification.event.smoke.DETECTED then + event = SmokeDetector.smoke.detected() + end + end + + -- CO + if cmd.args.notification_type == Notification.notification_type.CO then + if cmd.args.event == Notification.event.co.STATE_IDLE then + event = CarbonMonoxideDetector.carbonMonoxide.clear() + elseif cmd.args.event == Notification.event.co.CARBON_MONOXIDE_DETECTED then + event = CarbonMonoxideDetector.carbonMonoxide.detected() + end + end + + -- CO2 + if cmd.args.notification_type == Notification.notification_type.CO2 then + if cmd.args.event == Notification.event.co2.STATE_IDLE then + event = capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern.good() + elseif cmd.args.event == Notification.event.co2.CARBON_DIOXIDE_DETECTED then + event = capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern.moderate() + end + end + + -- DOOR_WINDOW/TILT + if cmd.args.notification_type == Notification.notification_type.ACCESS_CONTROL then + if cmd.args.event == Notification.event.access_control.WINDOW_DOOR_IS_CLOSED then + event = ContactSensor.contact.closed() + elseif cmd.args.event == Notification.event.access_control.WINDOW_DOOR_IS_OPEN then + event = ContactSensor.contact.open() + end + end + + -- PANIC + if cmd.args.notification_type == Notification.notification_type.EMERGENCY then + if cmd.args.event == Notification.event.emergency.STATE_IDLE then + event = PanicAlarm.panicAlarm.clear() + elseif cmd.args.event == Notification.event.emergency.PANIC_ALERT then + event = PanicAlarm.panicAlarm.panic() + end + end + + if (event ~= nil) then + device:emit_event(event) + end +end + +local function configuration_report_handler(self, device, cmd) + local param_number = cmd.args.parameter_number + local value = cmd.args.configuration_value + + if param_number == 10 then + local mapping = DEVICE_PROFILES[value] + if mapping then + set_profile(device, mapping) + end + end +end + +local aeotec_water_sensor_8 = { + supported_capabilities = { + capabilities.powerSource, + capabilities.carbonMonoxideDetector, + capabilities.carbonDioxideHealthConcern, + capabilities.soundDetection, + capabilities.panicAlarm + }, + zwave_handlers = { + [cc.CONFIGURATION] = { + [Configuration.REPORT] = configuration_report_handler + }, + [cc.NOTIFICATION] = { + [Notification.REPORT] = notification_report_handler + }, + }, + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh + } + }, + lifecycle_handlers = { + added = added_handler, + }, + NAME = "Aeotec Water Sensor 8", + can_handle = can_handle_aeotec_water_sensor_8 +} + +return aeotec_water_sensor_8 \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/src/init.lua b/drivers/SmartThings/zwave-sensor/src/init.lua index 213aa8c389..feb03115c2 100644 --- a/drivers/SmartThings/zwave-sensor/src/init.lua +++ b/drivers/SmartThings/zwave-sensor/src/init.lua @@ -154,6 +154,9 @@ local driver_template = { lazy_load_if_possible("wakeup-no-poll"), lazy_load_if_possible("firmware-version"), lazy_load_if_possible("apiv6_bugfix"), + lazy_load_if_possible("aeotec-door-window-sensor-8"), + lazy_load_if_possible("aeotec-aerq-8"), + lazy_load_if_possible("aeotec-water-sensor-8") }, lifecycle_handlers = { added = added_handler, diff --git a/drivers/SmartThings/zwave-sensor/src/preferences.lua b/drivers/SmartThings/zwave-sensor/src/preferences.lua index 9585b6ffe9..b45b73e089 100644 --- a/drivers/SmartThings/zwave-sensor/src/preferences.lua +++ b/drivers/SmartThings/zwave-sensor/src/preferences.lua @@ -175,7 +175,73 @@ local devices = { lowBatteryAlert = { parameter_number = 4, size = 1 }, }, }, + AEOTEC_AERQ_8 = { + MATCHING_MATRIX = { + mfrs = 0x0371, + product_types = {0x0002, 0x0102, 0x0202}, + product_ids = 0x0018 + }, + PARAMETERS = { + parameter1 = {parameter_number = 1, size = 1}, + parameter2 = {parameter_number = 2, size = 1}, + parameter3 = {parameter_number = 3, size = 1}, + parameter4 = {parameter_number = 4, size = 1}, + parameter13 = {parameter_number = 13, size = 1}, + parameter23 = {parameter_number = 23, size = 1}, + parameter24 = {parameter_number = 24, size = 4}, + parameter25 = {parameter_number = 25, size = 2}, + parameter26 = {parameter_number = 26, size = 2}, + parameter64 = {parameter_number = 64, size = 1}, + } + }, + AEOTEC_DOOR_WINDOW_SENSOR_8 = { + MATCHING_MATRIX = { + mfrs = 0x0371, + product_types = {0x0002, 0x0102, 0x0202}, + product_ids = {0x0037} + }, + PARAMETERS = { + parameter1 = {parameter_number = 1, size = 4}, + parameter2 = {parameter_number = 2, size = 1}, + parameter3 = {parameter_number = 3, size = 1}, + parameter5 = {parameter_number = 5, size = 1}, + parameter13 = {parameter_number = 13, size = 1}, + parameter23 = {parameter_number = 23, size = 1}, + parameter24 = {parameter_number = 24, size = 4}, + parameter25 = {parameter_number = 25, size = 2}, + parameter26 = {parameter_number = 26, size = 2}, + parameter27 = {parameter_number = 27, size = 1}, + parameter28 = {parameter_number = 28, size = 1}, + parameter33 = {parameter_number = 33, size = 1}, + parameter34 = {parameter_number = 34, size = 1}, + parameter35 = {parameter_number = 35, size = 1}, + parameter36 = {parameter_number = 36, size = 1}, + parameter64 = {parameter_number = 64, size = 1}, + }, + }, + AEOTEC_WATER_SENSOR_8 = { + MATCHING_MATRIX = { + mfrs = 0x0371, + product_types = {0x0002, 0x0102, 0x0202}, + product_ids = {0x0038} + }, + PARAMETERS = { + parameter1 = {parameter_number = 1, size = 1}, + parameter2 = {parameter_number = 2, size = 1}, + parameter3 = {parameter_number = 3, size = 1}, + parameter4 = {parameter_number = 4, size = 1}, + parameter5 = {parameter_number = 5, size = 1}, + parameter10 = {parameter_number = 10, size = 1}, + parameter13 = {parameter_number = 13, size = 1}, + parameter23 = {parameter_number = 23, size = 1}, + parameter24 = {parameter_number = 24, size = 4}, + parameter25 = {parameter_number = 25, size = 2}, + parameter26 = {parameter_number = 26, size = 2}, + parameter64 = {parameter_number = 64, size = 1}, + }, + }, } + local preferences = {} preferences.update_preferences = function(driver, device, args) diff --git a/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_aerq_8.lua b/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_aerq_8.lua new file mode 100644 index 0000000000..2529cb290b --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_aerq_8.lua @@ -0,0 +1,293 @@ +-- 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 test = require "integration_test" +local capabilities = require "st.capabilities" +local zw = require "st.zwave" +local zw_test_utils = require "integration_test.zwave_test_utils" +--- @type st.zwave.CommandClass.Notification +local Notification = (require "st.zwave.CommandClass.Notification")({ version = 3 }) +--- @type st.zwave.CommandClass.Battery +local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 }) +--- @type st.zwave.CommandClass.SensorMultilevel +local SensorMultilevel = (require "st.zwave.CommandClass.SensorMultilevel")({ version = 11 }) +--- @type st.zwave.CommandClass.Configuration +local Configuration = (require "st.zwave.CommandClass.Configuration")({ version = 4 }) +local t_utils = require "integration_test.utils" + +local sensor_endpoints = { + { + command_classes = { + {value = zw.BATTERY}, + {value = zw.NOTIFICATION}, + {value = zw.SENSOR_MULTILEVEL}, + {value = zw.CONFIGURATION} + } + } +} + +local mock_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-aerq-8.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_id = 0x0039, +}) + +local function test_init() + test.mock_device.add_test_device(mock_sensor) +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test( + "Device added lifecycle event for profile", + function() + test.socket.device_lifecycle:__queue_receive({ mock_sensor.id, "added" }) + test.socket.zwave:__expect_send( + zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Configuration:Get({ + parameter_number = 10 + }) + ) + ) + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.supportedMoldValues({"good", "moderate"})) + ) + + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.moldHealthConcern.good()) + ) + + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", capabilities.powerSource.powerSource.battery()) + ) + + + test.socket.zwave:__expect_send( + zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Battery:Get({}) + ) + ) + end +) + +test.register_message_test( + "Refresh should generate the correct commands", + { + { + channel = "capability", + direction = "receive", + message = { + mock_sensor.id, + { capability = "refresh", command = "refresh", args = {} } + } + }, + { + channel = "zwave", + direction = "send", + message = zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Battery:Get({}) + ) + }, + }, + { + inner_block_ordering = "relaxed" + } +) + +test.register_message_test( + "Battery report should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Battery:Report({ battery_level = 0x63 })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.battery.battery(99)) + } + } +) + +test.register_message_test( + "Notification report AC_MAINS_DISCONNECTED event should be handled power source state battery", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.AC_MAINS_DISCONNECTED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.powerSource.powerSource.battery()) + } + } +) + +test.register_message_test( + "Notification report AC_MAINS_RE_CONNECTED event should be handled power source state dc", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.AC_MAINS_RE_CONNECTED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.powerSource.powerSource.dc()) + } + } +) + +test.register_message_test( + "Notification report POWER_HAS_BEEN_APPLIED event should be send battery get", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.POWER_HAS_BEEN_APPLIED, + })) } + }, + { + channel = "zwave", + direction = "send", + message = zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Battery:Get({}) + ) + } + } +) + +test.register_message_test( + "Temperature reports should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.TEMPERATURE, + scale = SensorMultilevel.scale.temperature.CELSIUS, + sensor_value = 21.5 })) + } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.temperatureMeasurement.temperature({ value = 21.5, unit = 'C' })) + }, + { + channel = "devices", + direction = "send", + message = { + "register_native_capability_attr_handler", + { device_uuid = mock_sensor.id, capability_id = "temperatureMeasurement", capability_attr_id = "temperature" } + } + } + } +) + +test.register_message_test( + "Humidity reports should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.RELATIVE_HUMIDITY, + sensor_value = 70 })) + } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.relativeHumidityMeasurement.humidity({ value = 70, })) + }, + } +) + +test.register_message_test( + "Sensor multilevel reports dew_point type command should be handled as dew point measurement", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.DEW_POINT, + sensor_value = 8, + scale = 0 + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.dewPoint.dewpoint({value = 8, unit = "C"})) + } + } +) + +test.register_message_test( + "Notification report type WEATHER_ALARM event STATE_IDLE should be handled mold healt concern state good", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.WEATHER_ALARM, + event = Notification.event.weather_alarm.STATE_IDLE, + }))} + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.moldHealthConcern.good()) + } + } +) + +test.register_message_test( + "Notification report type WEATHER_ALARM event MOISTURE_ALARM should be handled mold healt concern state moderate", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.WEATHER_ALARM, + event = Notification.event.weather_alarm.MOISTURE_ALARM, + }))} + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.moldHealthConcern.moderate()) + } + } +) + +test.run_registered_tests() \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_door_window_sesnor_8.lua b/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_door_window_sesnor_8.lua new file mode 100644 index 0000000000..44ae9f52ae --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_door_window_sesnor_8.lua @@ -0,0 +1,430 @@ +-- 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 test = require "integration_test" +local capabilities = require "st.capabilities" +local zw = require "st.zwave" +local zw_test_utils = require "integration_test.zwave_test_utils" +--- @type st.zwave.CommandClass.Notification +local Notification = (require "st.zwave.CommandClass.Notification")({ version = 3 }) +--- @type st.zwave.CommandClass.Battery +local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 }) +--- @type st.zwave.CommandClass.SensorMultilevel +local SensorMultilevel = (require "st.zwave.CommandClass.SensorMultilevel")({ version = 11 }) +--- @type st.zwave.CommandClass.Configuration +local Configuration = (require "st.zwave.CommandClass.Configuration")({ version = 4 }) +local t_utils = require "integration_test.utils" + +local sensor_endpoints = { + { + command_classes = { + {value = zw.BATTERY}, + {value = zw.NOTIFICATION}, + {value = zw.SENSOR_MULTILEVEL}, + {value = zw.CONFIGURATION} + } + } +} + +local mock_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-door-window-sensor-8.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_id = 0x0037, +}) + +local function test_init() + test.mock_device.add_test_device(mock_sensor) +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test( + "Device added lifecycle event for profile", + function() + test.socket.device_lifecycle:__queue_receive({ mock_sensor.id, "added" }) + test.socket.zwave:__expect_send( + zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Configuration:Get({ + parameter_number = 10 + }) + ) + ) + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.supportedMoldValues({"good", "moderate"})) + ) + + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.moldHealthConcern.good()) + ) + + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", capabilities.powerSource.powerSource.battery()) + ) + + + test.socket.zwave:__expect_send( + zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Battery:Get({}) + ) + ) + end +) + +test.register_coroutine_test( + "Device init lifecycle event", + function() + test.socket.device_lifecycle:__queue_receive({ mock_sensor.id, "init" }) + + mock_sensor:set_field("three_axis_x", 0) + mock_sensor:set_field("three_axis_y", 0) + mock_sensor:set_field("three_axis_z", 0) + end +) + +test.register_message_test( + "Refresh should generate the correct commands", + { + { + channel = "capability", + direction = "receive", + message = { + mock_sensor.id, + { capability = "refresh", command = "refresh", args = {} } + } + }, + { + channel = "zwave", + direction = "send", + message = zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Battery:Get({}) + ) + }, + }, + { + inner_block_ordering = "relaxed" + } +) + +test.register_message_test( + "Notification report STATE_IDLE event should be handled as tamperAlert clear", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.HOME_SECURITY, + event = Notification.event.home_security.STATE_IDLE, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.tamperAlert.tamper.clear()) + } + } +) + +test.register_message_test( + "Notification report TAMPERING_PRODUCT_COVER_REMOVED event should be handled as tamperAlert detected", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.HOME_SECURITY, + event = Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.tamperAlert.tamper.detected()) + } + } +) + +test.register_message_test( + "Battery report should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Battery:Report({ battery_level = 0x63 })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.battery.battery(99)) + } + } +) + +test.register_message_test( + "Notification report AC_MAINS_DISCONNECTED event should be handled power source state battery", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.AC_MAINS_DISCONNECTED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.powerSource.powerSource.battery()) + } + } +) + +test.register_message_test( + "Notification report AC_MAINS_RE_CONNECTED event should be handled power source state dc", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.AC_MAINS_RE_CONNECTED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.powerSource.powerSource.dc()) + } + } +) + +test.register_message_test( + "Notification report POWER_HAS_BEEN_APPLIED event should be send battery get", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.POWER_HAS_BEEN_APPLIED, + })) } + }, + { + channel = "zwave", + direction = "send", + message = zw_test_utils.zwave_test_build_send_command( + mock_sensor, + Battery:Get({}) + ) + } + } +) + +test.register_message_test( + "Notification report WINDOW_DOOR_IS_OPEN event should be handled contact sensor state open", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.ACCESS_CONTROL, + event = Notification.event.access_control.WINDOW_DOOR_IS_OPEN, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.contactSensor.contact.open()) + } + } +) + +test.register_message_test( + "Notification report WINDOW_DOOR_IS_CLOSED event should be handled contact sensor state closed", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.ACCESS_CONTROL, + event = Notification.event.access_control.WINDOW_DOOR_IS_CLOSED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.contactSensor.contact.closed()) + } + } +) + +test.register_message_test( + "Temperature reports should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.TEMPERATURE, + scale = SensorMultilevel.scale.temperature.CELSIUS, + sensor_value = 21.5 })) + } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.temperatureMeasurement.temperature({ value = 21.5, unit = 'C' })) + }, + } +) + +test.register_message_test( + "Humidity reports should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.RELATIVE_HUMIDITY, + scale = SensorMultilevel.scale.relative_humidity.PERCENTAGE, + sensor_value = 70 })) + } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.relativeHumidityMeasurement.humidity({ value = 70, unit= '%' })) + }, + } +) + +test.register_message_test( + "Sensor multilevel reports dew_point type command should be handled as dew point measurement", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.DEW_POINT, + sensor_value = 8, + scale = 0 + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.dewPoint.dewpoint({value = 8, unit = "C"})) + } + } +) + +test.register_coroutine_test( + "Three Axis x reports should be correctly handled", + function() + test.socket.zwave:__queue_receive({ + mock_sensor.id, + SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.ACCELERATION_X_AXIS, + sensor_value = 200, + scale = SensorMultilevel.scale.acceleration_x_axis.METERS_PER_SQUARE_SECOND } + ) + }) + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", + capabilities.threeAxis.threeAxis({value = {200, 0, 0}, unit = 'mG'}) + ) + ) + end +) + +test.register_coroutine_test( + "Three Axis y reports should be correctly handled", + function() + test.socket.zwave:__queue_receive({ + mock_sensor.id, + SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.ACCELERATION_Y_AXIS, + sensor_value = 200, + scale = SensorMultilevel.scale.acceleration_y_axis.METERS_PER_SQUARE_SECOND } + ) + }) + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", + capabilities.threeAxis.threeAxis({value = {0, 200, 0}, unit = 'mG'}) + ) + ) + end +) + +test.register_coroutine_test( + "Three Axis z reports should be correctly handled", + function() + test.socket.zwave:__queue_receive({ + mock_sensor.id, + SensorMultilevel:Report({ + sensor_type = SensorMultilevel.sensor_type.ACCELERATION_Z_AXIS, + sensor_value = 400, + scale = SensorMultilevel.scale.acceleration_z_axis.METERS_PER_SQUARE_SECOND } + ) + }) + test.socket.capability:__expect_send( + mock_sensor:generate_test_message("main", + capabilities.threeAxis.threeAxis({value = {0, 0, 400}, unit = 'mG'}) + ) + ) + end +) + +test.register_message_test( + "Notification report type WEATHER_ALARM event STATE_IDLE should be handled mold healt concern state good", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.WEATHER_ALARM, + event = Notification.event.weather_alarm.STATE_IDLE, + }))} + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.moldHealthConcern.good()) + } + } +) + +test.register_message_test( + "Notification report type WEATHER_ALARM event MOISTURE_ALARM should be handled mold healt concern state moderate", + { + { + channel = "zwave", + direction = "receive", + message = { mock_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.WEATHER_ALARM, + event = Notification.event.weather_alarm.MOISTURE_ALARM, + }))} + }, + { + channel = "capability", + direction = "send", + message = mock_sensor:generate_test_message("main", capabilities.moldHealthConcern.moldHealthConcern.moderate()) + } + } +) + +test.run_registered_tests() \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_water_sensor_8.lua b/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_water_sensor_8.lua new file mode 100644 index 0000000000..ca6aeb8169 --- /dev/null +++ b/drivers/SmartThings/zwave-sensor/src/test/test_aeotec_water_sensor_8.lua @@ -0,0 +1,511 @@ +-- 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 test = require "integration_test" +local capabilities = require "st.capabilities" +local zw = require "st.zwave" +local zw_test_utils = require "integration_test.zwave_test_utils" +--- @type st.zwave.CommandClass.Notification +local Notification = (require "st.zwave.CommandClass.Notification")({ version = 3 }) +--- @type st.zwave.CommandClass.Battery +local Battery = (require "st.zwave.CommandClass.Battery")({ version = 1 }) +--- @type st.zwave.CommandClass.Configuration +local Configuration = (require "st.zwave.CommandClass.Configuration")({ version = 4 }) +local t_utils = require "integration_test.utils" + +local sensor_endpoints = { + { + command_classes = { + {value = zw.BATTERY}, + {value = zw.NOTIFICATION}, + {value = zw.SENSOR_MULTILEVEL}, + {value = zw.CONFIGURATION} + } + } +} + +local mock_water_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local mock_co_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8-co.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local mock_co2_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8-co2.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local mock_contact_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8-contact.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local mock_glass_break_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8-glass-break.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local mock_motion_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8-motion.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local mock_panic_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8-panic.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local mock_smoke_sensor = test.mock_device.build_test_zwave_device({ + profile = t_utils.get_profile_definition("aeotec-water-sensor-8-smoke.yml"), + zwave_endpoints = sensor_endpoints, + zwave_manufacturer_id = 0x0371, + zwave_product_type = 0x0002, + zwave_product_id = 0x0038, +}) + +local DEVICE_PROFILES = { + [0] = { + profile = "aeotec-water-sensor-8", + mock_device = mock_water_sensor, + default_cap = capabilities.waterSensor.water.dry(), + active_cap = capabilities.waterSensor.water.wet(), + default_cap_str = "dry", + active_cap_str = "wet", + notification_typ = Notification.notification_type.WATER, + on_event = Notification.event.water.LEAK_DETECTED, + off_event = Notification.event.water.STATE_IDLE + }, + [1] = { + profile = "aeotec-water-sensor-8-smoke", + mock_device = mock_smoke_sensor, + default_cap = capabilities.smokeDetector.smoke.clear(), + active_cap = capabilities.smokeDetector.smoke.detected(), + default_cap_str = "clear", + active_cap_str = "detected", + notification_typ = Notification.notification_type.SMOKE, + on_event = Notification.event.smoke.DETECTED, + off_event = Notification.event.smoke.STATE_IDLE + }, + [2] = { + profile = "aeotec-water-sensor-8-co", + mock_device = mock_co_sensor, + default_cap = capabilities.carbonMonoxideDetector.carbonMonoxide.clear(), + active_cap = capabilities.carbonMonoxideDetector.carbonMonoxide.detected(), + default_cap_str = "clear", + active_cap_str = "detected", + notification_typ = Notification.notification_type.CO, + on_event = Notification.event.co.CARBON_MONOXIDE_DETECTED, + off_event = Notification.event.co.STATE_IDLE + }, + [3] = { + profile = "aeotec-water-sensor-8-co2", + mock_device = mock_co2_sensor, + default_cap = capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern.good(), + active_cap = capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern.moderate(), + default_cap_str = "good", + active_cap_str = "moderate", + notification_typ = Notification.notification_type.CO2, + on_event = Notification.event.co2.CARBON_DIOXIDE_DETECTED, + off_event = Notification.event.co2.STATE_IDLE + }, + [4] = { + profile = "aeotec-water-sensor-8-contact", + mock_device = mock_contact_sensor, + default_cap = capabilities.contactSensor.contact.closed(), + active_cap = capabilities.contactSensor.contact.open(), + default_cap_str = "closed", + active_cap_str = "open", + notification_typ = Notification.notification_type.ACCESS_CONTROL, + on_event = Notification.event.access_control.WINDOW_DOOR_IS_OPEN, + off_event = Notification.event.access_control.WINDOW_DOOR_IS_CLOSED + }, + [5] = { + profile = "aeotec-water-sensor-8-contact", + mock_device = mock_contact_sensor, + default_cap = capabilities.contactSensor.contact.closed(), + active_cap = capabilities.contactSensor.contact.open(), + default_cap_str = "closed", + active_cap_str = "open", + notification_typ = Notification.notification_type.ACCESS_CONTROL, + on_event = Notification.event.access_control.WINDOW_DOOR_IS_OPEN, + off_event = Notification.event.access_control.WINDOW_DOOR_IS_CLOSED + }, + [6] = { + profile = "aeotec-water-sensor-8-motion", + mock_device = mock_motion_sensor, + default_cap = capabilities.motionSensor.motion.inactive(), + active_cap = capabilities.motionSensor.motion.active(), + default_cap_str = "inactive", + active_cap_str = "active", + notification_typ = Notification.notification_type.HOME_SECURITY, + on_event = Notification.event.home_security.MOTION_DETECTION, + off_event = Notification.event.home_security.STATE_IDLE + }, + [7] = { + profile = "aeotec-water-sensor-8-glass-break", + mock_device = mock_glass_break_sensor, + default_cap = capabilities.soundDetection.soundDetected.noSound(), + active_cap = capabilities.soundDetection.soundDetected.glassBreaking(), + default_cap_str = "noSound", + active_cap_str = "glassBreaking", + notification_typ = Notification.notification_type.HOME_SECURITY, + on_event = Notification.event.home_security.GLASS_BREAKAGE, + off_event = Notification.event.home_security.STATE_IDLE + }, + [8] = { + profile = "aeotec-water-sensor-8-panic", + mock_device = mock_panic_sensor, + default_cap = capabilities.panicAlarm.panicAlarm.clear(), + active_cap = capabilities.panicAlarm.panicAlarm.panic(), + default_cap_str = "clear", + active_cap_str = "panic", + notification_typ = Notification.notification_type.EMERGENCY, + on_event = Notification.event.emergency.PANIC_ALERT, + off_event = Notification.event.emergency.STATE_IDLE + } +} + +local function test_init() + test.mock_device.add_test_device(mock_water_sensor) + test.mock_device.add_test_device(mock_smoke_sensor) + test.mock_device.add_test_device(mock_co_sensor) + test.mock_device.add_test_device(mock_co2_sensor) + test.mock_device.add_test_device(mock_contact_sensor) + test.mock_device.add_test_device(mock_contact_sensor) + test.mock_device.add_test_device(mock_motion_sensor) + test.mock_device.add_test_device(mock_glass_break_sensor) + test.mock_device.add_test_device(mock_panic_sensor) +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test( + "Device added lifecycle event for profile", + function() + test.socket.device_lifecycle:__queue_receive({ mock_water_sensor.id, "added" }) + test.socket.zwave:__expect_send( + zw_test_utils.zwave_test_build_send_command( + mock_water_sensor, + Configuration:Get({ + parameter_number = 10 + }) + ) + ) + test.socket.capability:__expect_send( + mock_water_sensor:generate_test_message("main", capabilities.moldHealthConcern.supportedMoldValues({"good", "moderate"})) + ) + test.socket.capability:__expect_send( + mock_water_sensor:generate_test_message("main", capabilities.moldHealthConcern.moldHealthConcern.good()) + ) + test.socket.capability:__expect_send( + mock_water_sensor:generate_test_message("main", capabilities.powerSource.powerSource.battery()) + ) + test.socket.zwave:__expect_send( + zw_test_utils.zwave_test_build_send_command( + mock_water_sensor, + Battery:Get({}) + ) + ) + end +) + +test.register_message_test( + "Refresh should generate the correct commands", + { + { + channel = "capability", + direction = "receive", + message = { + mock_water_sensor.id, + { capability = "refresh", command = "refresh", args = {} } + } + }, + { + channel = "zwave", + direction = "send", + message = zw_test_utils.zwave_test_build_send_command( + mock_water_sensor, + Battery:Get({}) + ) + }, + }, + { + inner_block_ordering = "relaxed" + } +) + +test.register_message_test( + "Notification report STATE_IDLE event should be handled tamper alert state clear", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.HOME_SECURITY, + event = Notification.event.home_security.STATE_IDLE, + event_parameter = string.char(Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED) + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_water_sensor:generate_test_message("main", capabilities.tamperAlert.tamper.clear()) + } + } +) + +test.register_message_test( + "Notification report STATE_IDLE event should be handled tamper alert state clear", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.HOME_SECURITY, + event = Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED, + event_parameter = string.char(Notification.event.home_security.STATE_IDLE) + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_water_sensor:generate_test_message("main", capabilities.tamperAlert.tamper.detected()) + } + } +) + +test.register_message_test( + "Battery report should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Battery:Report({ battery_level = 0x63 })) } + }, + { + channel = "capability", + direction = "send", + message = mock_water_sensor:generate_test_message("main", capabilities.battery.battery(99)) + } + } +) + +test.register_message_test( + "Notification report AC_MAINS_DISCONNECTED event should be handled power source state battery", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.AC_MAINS_DISCONNECTED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_water_sensor:generate_test_message("main", capabilities.powerSource.powerSource.battery()) + } + } +) + +test.register_message_test( + "Notification report AC_MAINS_RE_CONNECTED event should be handled power source state dc", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.AC_MAINS_RE_CONNECTED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_water_sensor:generate_test_message("main", capabilities.powerSource.powerSource.dc()) + } + } +) + +test.register_message_test( + "Notification report POWER_HAS_BEEN_APPLIED event should be send battery get", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.POWER_MANAGEMENT, + event = Notification.event.power_management.POWER_HAS_BEEN_APPLIED, + })) } + }, + { + channel = "zwave", + direction = "send", + message = zw_test_utils.zwave_test_build_send_command( + mock_water_sensor, + Battery:Get({}) + ) + } + } +) + +test.register_message_test( + "Notification report LEAK_DETECTED event should be handled water sensor state wet", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.WATER, + event = Notification.event.water.LEAK_DETECTED, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_water_sensor:generate_test_message("main", capabilities.waterSensor.water.wet()) + } + } +) + +test.register_message_test( + "Notification report STATE_IDLE event should be handled water sensor state dry", + { + { + channel = "zwave", + direction = "receive", + message = { mock_water_sensor.id, zw_test_utils.zwave_test_build_receive_command(Notification:Report({ + notification_type = Notification.notification_type.WATER, + event = Notification.event.water.STATE_IDLE, + })) } + }, + { + channel = "capability", + direction = "send", + message = mock_water_sensor:generate_test_message("main", capabilities.waterSensor.water.dry()) + } + } +) + +for param_value, data in pairs(DEVICE_PROFILES) do + local value = param_value + local profile = data.profile + local mock_device = data.mock_device + local default_cap = data.default_cap + local active_cap = data.active_cap + local notification_type = data.notification_typ + local on_event = data.on_event + local off_event = data.off_event + + + test.register_coroutine_test( + "Profile should update when Configuration Report parameter 10 = " .. value, + function() + test.socket.zwave:__queue_receive({ + mock_device.id, + Configuration:Report({ + parameter_number = 10, + configuration_value = value + }) + }) + + test.socket.device_lifecycle:__expect_send( + mock_device:expect_metadata_update({ + profile = profile + }) + ) + + if profile == "aeotec-water-sensor-8-glass-break" then + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.soundDetection.supportedSoundTypes({"noSound", "glassBreaking"})) + ) + elseif profile == "aeotec-water-sensor-8-co2" then + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.carbonDioxideHealthConcern.supportedCarbonDioxideValues({"good", "moderate"})) + ) + end + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", default_cap) + ) + end + ) + + test.register_coroutine_test( + "Notification report type " .. notification_type .. " event ".. off_event .. " should be handled " .. data.default_cap_str, + function() + mock_device:set_field("active_profile", profile) + + test.socket.zwave:__queue_receive({ + mock_device.id, + Notification:Report({ + notification_type = notification_type, + event = off_event, + event_parameter = string.char(on_event) + }) + }) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", default_cap) + ) + end + ) + + test.register_coroutine_test( + "Notification report type " .. notification_type .. " event ".. on_event .. " should be handled " .. data.active_cap_str, + function() + mock_device:set_field("active_profile", profile) + + test.socket.zwave:__queue_receive({ + mock_device.id, + Notification:Report({ + notification_type = notification_type, + event = on_event, + }) + }) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", active_cap) + ) + end + ) +end + +test.run_registered_tests() \ No newline at end of file diff --git a/drivers/SmartThings/zwave-sensor/src/timed-tamper-clear/init.lua b/drivers/SmartThings/zwave-sensor/src/timed-tamper-clear/init.lua index 2007bedb0d..cfa5c3ee76 100644 --- a/drivers/SmartThings/zwave-sensor/src/timed-tamper-clear/init.lua +++ b/drivers/SmartThings/zwave-sensor/src/timed-tamper-clear/init.lua @@ -20,22 +20,57 @@ local capabilities = require "st.capabilities" local TAMPER_TIMER = "_tamper_timer" local TAMPER_CLEAR = 10 -local FIBARO_DOOR_WINDOW_MFR_ID = 0x010F -local function can_handle_tamper_event(opts, driver, device, cmd, ...) - if device.zwave_manufacturer_id ~= FIBARO_DOOR_WINDOW_MFR_ID and - opts.dispatcher_class == "ZwaveDispatcher" and +local excluded_devices = { + FIBARO_DOOR_WINDOW = { + mfrs = 0x010F + }, + AEOTEC_AERQ_8 = { + mfrs = 0x0371, + product_ids = 0x0018 + }, + AEOTEC_DOOR_WINDOW_SENSOR_8 = { + mfrs = 0x0371, + product_ids = 0x0037 + }, + AEOTEC_WATER_SENSOR_8 = { + mfrs = 0x0371, + product_ids = 0x0038 + }, +} + +local function can_handle_tamper_event(opts, driver, zw_device, cmd, ...) + -- check only for relevant tamper event first + if not(opts.dispatcher_class == "ZwaveDispatcher" and cmd ~= nil and cmd.cmd_class ~= nil and cmd.cmd_class == cc.NOTIFICATION and cmd.cmd_id == Notification.REPORT and cmd.args.notification_type == Notification.notification_type.HOME_SECURITY and (cmd.args.event == Notification.event.home_security.TAMPERING_PRODUCT_COVER_REMOVED or - cmd.args.event == Notification.event.home_security.TAMPERING_PRODUCT_MOVED) then - local subdriver = require("timed-tamper-clear") - return true, subdriver - else return false + cmd.args.event == Notification.event.home_security.TAMPERING_PRODUCT_MOVED)) then + return false + end + + -- check exclusion list: if device matches any entry, skip auto-clear + for _, excluded_device in pairs(excluded_devices) do + local mfrs = excluded_device.mfrs + local product_types = excluded_device.product_types or nil + local product_ids = excluded_device.product_ids or nil + + if mfrs ~= nil then + if zw_device:id_match( + mfrs, + product_types, + product_ids + ) then + return false + end end + end + + local subdriver = require("timed-tamper-clear") + return true, subdriver end -- This behavior is from zwave-door-window-sensor.groovy. We've seen this behavior diff --git a/drivers/SmartThings/zwave-switch/src/init.lua b/drivers/SmartThings/zwave-switch/src/init.lua index 26cca570a7..2132006db1 100644 --- a/drivers/SmartThings/zwave-switch/src/init.lua +++ b/drivers/SmartThings/zwave-switch/src/init.lua @@ -17,6 +17,8 @@ local SwitchMultilevel = (require "st.zwave.CommandClass.SwitchMultilevel")({ ve local preferencesMap = require "preferences" local configurationsMap = require "configurations" +local lazy_load_if_possible = require "lazy_load_subdriver" + --- Map component to end_points(channels) --- --- @param device st.zwave.Device diff --git a/drivers/SmartThings/zwave-switch/src/test/test_inovelli_dimmer_scenes.lua b/drivers/SmartThings/zwave-switch/src/test/test_inovelli_dimmer_scenes.lua index 681bb514a0..aec305f251 100644 --- a/drivers/SmartThings/zwave-switch/src/test/test_inovelli_dimmer_scenes.lua +++ b/drivers/SmartThings/zwave-switch/src/test/test_inovelli_dimmer_scenes.lua @@ -185,7 +185,7 @@ test.register_message_test( channel = "zwave", direction = "receive", message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ - scene_number = BUTTON_DOWN_SCENE_1, + scene_number = BUTTON_UP_SCENE_2, key_attributes=CentralScene.key_attributes.KEY_PRESSED_2_TIMES})) } }, @@ -208,7 +208,7 @@ test.register_message_test( channel = "zwave", direction = "receive", message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ - scene_number = BUTTON_DOWN_SCENE_1, + scene_number = BUTTON_UP_SCENE_2, key_attributes=CentralScene.key_attributes.KEY_PRESSED_3_TIMES})) } }, @@ -231,7 +231,7 @@ test.register_message_test( channel = "zwave", direction = "receive", message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ - scene_number = BUTTON_DOWN_SCENE_1, + scene_number = BUTTON_UP_SCENE_2, key_attributes=CentralScene.key_attributes.KEY_PRESSED_4_TIMES})) } }, @@ -254,7 +254,7 @@ test.register_message_test( channel = "zwave", direction = "receive", message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ - scene_number = BUTTON_DOWN_SCENE_1, + scene_number = BUTTON_UP_SCENE_2, key_attributes=CentralScene.key_attributes.KEY_PRESSED_5_TIMES})) } }, @@ -270,6 +270,106 @@ test.register_message_test( } ) +test.register_message_test( + "Central Scene notification Button held should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ + scene_number = BUTTON_DOWN_SCENE_1, + key_attributes=CentralScene.key_attributes.KEY_PRESSED_1_TIME})) + } + }, + { + channel = "capability", + direction = "send", + message = mock_inovelli_dimmer:generate_test_message("button1", capabilities.button.button.pushed({ + state_change = true })) + } + } +) + +test.register_message_test( + "Central Scene notification Button held should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ + scene_number = BUTTON_DOWN_SCENE_1, + key_attributes=CentralScene.key_attributes.KEY_PRESSED_2_TIMES})) + } + }, + { + channel = "capability", + direction = "send", + message = mock_inovelli_dimmer:generate_test_message("button1", capabilities.button.button.pushed_2x({ + state_change = true })) + } + } +) + +test.register_message_test( + "Central Scene notification Button held should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ + scene_number = BUTTON_DOWN_SCENE_1, + key_attributes=CentralScene.key_attributes.KEY_PRESSED_3_TIMES})) + } + }, + { + channel = "capability", + direction = "send", + message = mock_inovelli_dimmer:generate_test_message("button1", capabilities.button.button.pushed_3x({ + state_change = true })) + } + } +) + +test.register_message_test( + "Central Scene notification Button held should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ + scene_number = BUTTON_DOWN_SCENE_1, + key_attributes=CentralScene.key_attributes.KEY_PRESSED_4_TIMES})) + } + }, + { + channel = "capability", + direction = "send", + message = mock_inovelli_dimmer:generate_test_message("button1", capabilities.button.button.pushed_4x({ + state_change = true })) + } + } +) + +test.register_message_test( + "Central Scene notification Button held should be handled", + { + { + channel = "zwave", + direction = "receive", + message = { mock_inovelli_dimmer.id, zw_test_utils.zwave_test_build_receive_command(CentralScene:Notification({ + scene_number = BUTTON_DOWN_SCENE_1, + key_attributes=CentralScene.key_attributes.KEY_PRESSED_5_TIMES})) + } + }, + { + channel = "capability", + direction = "send", + message = mock_inovelli_dimmer:generate_test_message("button1", capabilities.button.button.pushed_5x({ + state_change = true })) + } + } +) + test.register_message_test( "Central Scene notification Button held should be handled", {