From ec16d4e1a7f75771077ae5f971c621c34e79a381 Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Mon, 6 Oct 2025 08:29:11 +0200 Subject: [PATCH 01/13] Add support to frient vibration sensor --- .../zigbee-contact/fingerprints.yml | 8 +- ...cceleration-motion-temperature-battery.yml | 41 ++ .../zigbee-contact/src/configurations.lua | 17 +- .../src/frient/frient-vibration/init.lua | 225 +++++++++ .../zigbee-contact/src/frient/init.lua | 5 +- .../src/test/test_frient_vibration_sensor.lua | 426 ++++++++++++++++++ 6 files changed, 716 insertions(+), 6 deletions(-) create mode 100644 drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml create mode 100644 drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua create mode 100644 drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua diff --git a/drivers/SmartThings/zigbee-contact/fingerprints.yml b/drivers/SmartThings/zigbee-contact/fingerprints.yml index f35ca2f7ba..ff3faed09b 100644 --- a/drivers/SmartThings/zigbee-contact/fingerprints.yml +++ b/drivers/SmartThings/zigbee-contact/fingerprints.yml @@ -149,11 +149,11 @@ zigbeeManufacturer: manufacturer: frient A/S model: WISZB-121 deviceProfileName: contact-battery-profile - - id: "frient A/S/WISZB-131" - deviceLabel: frient Entry Sensor 2 Pro + - id: "frient A/S/WISZB-137" + deviceLabel: frient Vibration Sensor manufacturer: frient A/S - model: WISZB-131 - deviceProfileName: frient-contact-battery-temperature + model: WISZB-137 + deviceProfileName: acceleration-motion-temperature-battery - id: "Compacta/ZBWDS" deviceLabel: Smartenit Open/Closed Sensor manufacturer: Compacta diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml new file mode 100644 index 0000000000..13506508c7 --- /dev/null +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml @@ -0,0 +1,41 @@ +name: acceleration-motion-temperature-battery +components: +- id: main + capabilities: + - id: accelerationSensor + version: 1 + - id: motionSensor + version: 1 + - id: threeAxis + version: 1 + - id: temperatureMeasurement + version: 1 + - id: battery + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: VibrationsSensor +preferences: + - preferenceId: tempOffset + explicit: true + - title: "Temperature sensitivity (°)" + name: temperatureSensitivity + description: "Minimum change in temperature to report" + required: false + preferenceType: number + definition: + minimum: 0.1 + maximum: 2.0 + default: 1.0 + - title: "Sensitivity level" + name: sensitivityLevel + description: "How sensitivite the device is to vibrations" + required: false + preferenceType: integer + definition: + minimum: 1 + maximum: 15 + default: 10 \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-contact/src/configurations.lua b/drivers/SmartThings/zigbee-contact/src/configurations.lua index aa28e1e43f..d998335062 100644 --- a/drivers/SmartThings/zigbee-contact/src/configurations.lua +++ b/drivers/SmartThings/zigbee-contact/src/configurations.lua @@ -92,7 +92,6 @@ local devices = { { mfr = "Sercomm Corp.", model = "SZ-DWS04" }, { mfr = "DAWON_DNS", model = "SS-B100-ZB" }, { mfr = "frient A/S", model = "WISZB-120" }, - { mfr = "frient A/S", model = "WISZB-131" }, { mfr = "Compacta", model = "ZBWDS" } }, CONFIGURATION = { @@ -129,6 +128,22 @@ local devices = { } } }, + FRIENT_VIBRATION_SENSOR_WISZB_137 = { + FINGERPRINTS = { + { mfr = "frient A/S", model = "WISZB-137" } + }, + CONFIGURATION = { + { + cluster = IASZone.ID, + attribute = IASZone.attributes.ZoneStatus.ID, + minimum_interval = 0, + maximum_interval = 3600, + data_type = IASZone.attributes.ZoneStatus.base_type, + reportable_change = 1, + endpoint = 0x2D + } + } + } } local configurations = {} diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua new file mode 100644 index 0000000000..f6f7318159 --- /dev/null +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -0,0 +1,225 @@ +-- 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" +local zcl_clusters = require "st.zigbee.zcl.clusters" +local cluster_base = require "st.zigbee.cluster_base" +local battery_defaults = require "st.zigbee.defaults.battery_defaults" +local device_management = require "st.zigbee.device_management" +local data_types = require "st.zigbee.data_types" +local log = require "log" +local util = require "st.utils" +local threeAxis = capabilities.threeAxis + +local TemperatureMeasurement = zcl_clusters.TemperatureMeasurement +local IASZone = zcl_clusters.IASZone +local PowerConfiguration = zcl_clusters.PowerConfiguration +local POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT = 0x2D +local TEMPERATURE_ENDPOINT = 0x26 + +local FRIENT_DEVICE_FINGERPRINTS = { + { mfr = "frient A/S", model = "WISZB-137", }, +} + +local function can_handle_vibration_sensor(opts, driver, device, ...) + for _, fingerprint in ipairs(FRIENT_DEVICE_FINGERPRINTS) do + if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then + return true + end + end + return false +end + +local Frient_AccelerationMeasurementCluster = { + ID = 0xFC04, + ManufacturerSpecificCode = 0x1015, + attributes = { + MeasuredValueX = { ID = 0x0000, data_type = data_types.name_to_id_map["Int16"] }, + MeasuredValueY = { ID = 0x0001, data_type = data_types.name_to_id_map["Int16"] }, + MeasuredValueZ = { ID = 0x0002, data_type = data_types.name_to_id_map["Int16"] } + }, +} + +local function acceleration_measure_value_attr_handler(driver, device, attr_val, zb_rx) + -- Initialize variables to store the axis values + local measured_x = device:get_field("measured_x") + local measured_y = device:get_field("measured_y") + local measured_z = device:get_field("measured_z") + + -- Process the attribute records + for _, attribute_record in ipairs(zb_rx.body.zcl_body.attr_records) do + local attribute_id = attribute_record.attr_id.value + local axis_value = attribute_record.data.value + + if attribute_id == 0x0000 then + measured_x = axis_value + device:set_field("measured_x", measured_x) + log.trace("Updated X value: " .. axis_value) + elseif attribute_id == 0x0001 then + measured_y = axis_value + device:set_field("measured_y", measured_y) + log.trace("Updated Y value: " .. axis_value) + elseif attribute_id == 0x0002 then + measured_z = axis_value + device:set_field("measured_z", measured_z) + log.trace("Updated Z value: " .. axis_value) + else + log.warn("Unknown AttributeId: " .. tostring(attribute_id)) + end + end + + -- Ensure all values are non-nil before emitting + if measured_x and measured_y and measured_z then + device:emit_event(threeAxis.threeAxis({measured_x, measured_y, measured_z})) + end +end + +local function get_cluster_configurations() + return { + { + cluster = Frient_AccelerationMeasurementCluster.ID, + attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, + minimum_interval = 0, + maximum_interval = 300, + reportable_change = 0x0001, + data_type = data_types.Int16, + mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + }, + { + cluster = Frient_AccelerationMeasurementCluster.ID, + attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, + minimum_interval = 0, + maximum_interval = 300, + reportable_change = 0x0001, + data_type = data_types.Int16, + mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + }, + { + cluster = Frient_AccelerationMeasurementCluster.ID, + attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, + minimum_interval = 0, + maximum_interval = 300, + reportable_change = 0x0001, + data_type = data_types.Int16, + mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + } + } +end + +local function generate_event_from_zone_status(driver, device, zone_status, zb_rx) + device:emit_event(zone_status:is_alarm1_set() and capabilities.motionSensor.motion.active() or capabilities.motionSensor.motion.inactive()) + device:emit_event(zone_status:is_alarm2_set() and capabilities.accelerationSensor.acceleration.active() or capabilities.accelerationSensor.acceleration.inactive()) +end + +local function ias_zone_status_attr_handler(driver, device, attr_val, zb_rx) + log.debug("Received IAS Zone Status:"..util.stringify_table(attr_val)) + generate_event_from_zone_status(driver, device, attr_val, zb_rx) +end + +local function ias_zone_status_change_handler(driver, device, zb_rx) + log.debug( "Received IAS Zone Change:"..util.stringify_table(zb_rx.body.zcl_body.zone_status)) + generate_event_from_zone_status(driver, device, zb_rx.body.zcl_body.zone_status, zb_rx) +end + +local function device_init(driver, device) + log.trace "Initializing sensor" + battery_defaults.build_linear_voltage_init(2.3, 3.0)(driver, device) + --Add the manufacturer-specific attributes to generate their configure reporting and bind requests + for _, config in pairs(get_cluster_configurations()) do + device:add_configured_attribute(config) + end +end + +local function do_refresh(driver, device) + log.trace "Refreshing vibration sensor attributes" + device:send(IASZone.attributes.ZoneStatus:read(device):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device):to_endpoint(TEMPERATURE_ENDPOINT)) + device:send(PowerConfiguration.attributes.BatteryVoltage:read(device)) +end + +local function do_configure(driver, device, event, args) + log.trace("Configuring sensor:" .. event) + device:configure() + + device:send(device_management.build_bind_request(device, zcl_clusters.IASZone.ID, driver.environment_info.hub_zigbee_eui, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(IASZone.attributes.ZoneStatus:configure_reporting(device, 0, 1*60*60, 1):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(device_management.build_bind_request(device, Frient_AccelerationMeasurementCluster.ID, driver.environment_info.hub_zigbee_eui, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + + local sensitivityLevel = device.preferences.sensitivityLevel or 10 + log.debug("Writing CurrentZoneSensitivityLevel attribute to: " .. sensitivityLevel) + device:send(IASZone.attributes.CurrentZoneSensitivityLevel:write(device, sensitivityLevel):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + + local sensitivity = math.floor((device.preferences.temperatureSensitivity or 0.1) * 100 + 0.5) + log.debug("Configuring temperature sensitivity: " .. sensitivity) + device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting(device, 30, 1 * 60 * 60, sensitivity):to_endpoint(TEMPERATURE_ENDPOINT)) + + device.thread:call_with_delay(5, function() + device:refresh() + end) +end + +local function info_changed(driver, device, event, args) + if args and args.old_st_store then + if args.old_st_store.preferences.sensitivityLevel ~= device.preferences.sensitivityLevel then + local sensitivityLevel = device.preferences.sensitivityLevel or 10 + log.debug("Writing Current Zone Sensitivity Level Attribute To: "..sensitivityLevel) + device:send(IASZone.attributes.CurrentZoneSensitivityLevel:write(device, sensitivityLevel):to_endpoint(0x2D)) + end + if args.old_st_store.preferences.temperatureSensitivity ~= device.preferences.temperatureSensitivity then + local sensitivity = math.floor((device.preferences.temperatureSensitivity or 0.1)*100 + 0.5) + log.debug("Configuring temperature sensitivity: "..sensitivity) + device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting(device, 30, 1*60*60, sensitivity):to_endpoint(0x26)) + end + device.thread:call_with_delay(5, function() + device:refresh() + end) + end +end + +local frient_vibration_driver_template = { + NAME = "frient vibration driver", + lifecycle_handlers = { + init = device_init, + doConfigure = do_configure, + infoChanged = info_changed + }, + zigbee_handlers = { + cluster = { + [IASZone.ID] = { + [IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler + } + }, + attr = { + [IASZone.ID] = { + [IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler + }, + [Frient_AccelerationMeasurementCluster.ID] = { + [Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID] = acceleration_measure_value_attr_handler, + [Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID] = acceleration_measure_value_attr_handler, + [Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID] = acceleration_measure_value_attr_handler, + } + } + }, + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = do_refresh, + } + }, + can_handle = can_handle_vibration_sensor +} + +return frient_vibration_driver_template \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-contact/src/frient/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/init.lua index e732664d91..33f362ca80 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/init.lua @@ -80,6 +80,9 @@ local frient_sensor = { doConfigure = do_configure, infoChanged = info_changed }, + sub_drivers = { + require("frient/frient-vibration"), + }, zigbee_handlers = { cluster = { [IASZone.ID] = { @@ -93,7 +96,7 @@ local frient_sensor = { } }, can_handle = function(opts, driver, device, ...) - return (device:get_manufacturer() == "frient A/S" and (device:get_model() == "WISZB-120" or device:get_model() == "WISZB-121" or device:get_model() == "WISZB-131")) + return (device:get_manufacturer() == "frient A/S") and (device:get_model() == "WISZB-120" or device:get_model() == "WISZB-121" or device:get_model() == "WISZB-137") end } diff --git a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua new file mode 100644 index 0000000000..c3745482fa --- /dev/null +++ b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua @@ -0,0 +1,426 @@ + +-- 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 clusters = require "st.zigbee.zcl.clusters" +local capabilities = require "st.capabilities" +local t_utils = require "integration_test.utils" +local zigbee_test_utils = require "integration_test.zigbee_test_utils" +local IasEnrollResponseCode = require "st.zigbee.generated.zcl_clusters.IASZone.types.EnrollResponseCode" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" + +local IASZone = clusters.IASZone +local PowerConfiguration = clusters.PowerConfiguration +local TemperatureMeasurement = clusters.TemperatureMeasurement +local POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT = 0x2D +local TEMPERATURE_ENDPOINT = 0x26 + +local base64 = require "base64" +local mock_device = test.mock_device.build_test_zigbee_device( + { profile = t_utils.get_profile_definition("acceleration-motion-temperature-battery.yml"), + zigbee_endpoints = { + [0x01] = { + id = 0x01, + manufacturer = "frient A/S", + model = "WISZB-137", + server_clusters = { 0x0003, 0x0005, 0x0006 } + }, + [0x2D] = { + id = 0x2D, + server_clusters = { 0x0000, 0x0001, 0x0003, 0x0020, 0x0500, 0xFC04 } + }, + [0x26] = { + id = 0x26, + server_clusters = { 0x0402 } + } + } + } +) + +local Frient_AccelerationMeasurementCluster = { + ID = 0xFC04, + ManufacturerSpecificCode = 0x1015, + attributes = { + MeasuredValueX = { ID = 0x0000, data_type = data_types.name_to_id_map["Int16"] }, + MeasuredValueY = { ID = 0x0001, data_type = data_types.name_to_id_map["Int16"] }, + MeasuredValueZ = { ID = 0x0002, data_type = data_types.name_to_id_map["Int16"] } + }, +} + +zigbee_test_utils.prepare_zigbee_env_info() + +local function test_init() + test.mock_device.add_test_device(mock_device) +end + +test.set_test_init_function(test_init) + +local function custom_configure_reporting(device, cluster, attribute, data_type, min_interval, max_interval, reportable_change, mfg_code) + local message = cluster_base.configure_reporting(device, + data_types.ClusterId(cluster), + data_types.AttributeId(attribute), + data_type, + min_interval, + max_interval, + reportable_change) + + -- Set the manufacturer-specific bit and add the manufacturer code + message.body.zcl_header.frame_ctrl:set_mfg_specific() + message.body.zcl_header.mfg_code = data_types.validate_or_build_type(mfg_code, data_types.Uint16, "mfg_code") + + return message +end + +test.register_coroutine_test( + "init and doConfigure lifecycles should be handled properly", + function() + test.socket.environment_update:__queue_receive({ "zigbee", { hub_zigbee_id = base64.encode(zigbee_test_utils.mock_hub_eui) } }) + test.socket.zigbee:__set_channel_ordering("relaxed") + + test.socket.device_lifecycle:__queue_receive({ mock_device.id, "init" }) + + test.wait_for_events() + + --test.socket.device_lifecycle:__queue_receive({ mock_device.id, "added" }) + test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request( + mock_device, + zigbee_test_utils.mock_hub_eui, + PowerConfiguration.ID, + POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + PowerConfiguration.attributes.BatteryVoltage:configure_reporting( + mock_device, + 30, + 21600, + 1 + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request( + mock_device, + zigbee_test_utils.mock_hub_eui, + TemperatureMeasurement.ID, + TEMPERATURE_ENDPOINT + ):to_endpoint(TEMPERATURE_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( + mock_device, + 30, + 600, + 100 + ):to_endpoint(TEMPERATURE_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + IASZone.attributes.IASCIEAddress:write( + mock_device, + zigbee_test_utils.mock_hub_eui + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request( + mock_device, + zigbee_test_utils.mock_hub_eui, + Frient_AccelerationMeasurementCluster.ID, + POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + custom_configure_reporting( + mock_device, + Frient_AccelerationMeasurementCluster.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.data_type, + 0x0000, + 0x012C, + 0x0001, + Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + custom_configure_reporting( + mock_device, + Frient_AccelerationMeasurementCluster.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.data_type, + 0x0000, + 0x012C, + 0x0001, + Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + custom_configure_reporting( + mock_device, + Frient_AccelerationMeasurementCluster.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.data_type, + 0x0000, + 0x012C, + 0x0001, + Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + IASZone.server.commands.ZoneEnrollResponse( + mock_device, + IasEnrollResponseCode.SUCCESS, + 0x00 + ) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request( + mock_device, + zigbee_test_utils.mock_hub_eui, + IASZone.ID, + POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT + ) + }) + + + test.socket.zigbee:__expect_send({ + mock_device.id, + IASZone.attributes.CurrentZoneSensitivityLevel:write( + mock_device, + 0x000A + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( + mock_device, + 0x001E, + 0x0E10, + 100 + ):to_endpoint(TEMPERATURE_ENDPOINT) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + IASZone.attributes.ZoneStatus:configure_reporting( + mock_device, + 0, + 3600, + 0 + ) + }) + + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request( + mock_device, + zigbee_test_utils.mock_hub_eui, + Frient_AccelerationMeasurementCluster.ID, + POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT + ) + }) + + mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + end +) + +test.register_message_test( + "Temperature report should be handled (C) for the temperature measurement cluster", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, TemperatureMeasurement.attributes.MeasuredValue:build_test_attr_report(mock_device, 2300)} + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.temperatureMeasurement.temperature({ value = 23.0, unit = "C"})) + }, + { + channel = "devices", + direction = "send", + message = { "register_native_capability_attr_handler", + { + device_uuid = mock_device.id, capability_id = "temperatureMeasurement", capability_attr_id = "temperature" + } + } + } + } +) + +test.register_message_test( + "Battery min voltage report should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = {mock_device.id, PowerConfiguration.attributes.BatteryVoltage:build_test_attr_report(mock_device, 23)} + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.battery.battery(0)) + } + } +) + +test.register_message_test( + "Battery max voltage report should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = {mock_device.id, PowerConfiguration.attributes.BatteryVoltage:build_test_attr_report(mock_device, 30)} + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.battery.battery(100)) + } + } +) + +test.register_coroutine_test( +"Refresh necessary attributes", +function() + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__queue_receive({ mock_device.id, { capability = "refresh", component = "main", command = "refresh", args = {} } }) + test.socket.zigbee:__expect_send({ mock_device.id, IASZone.attributes.ZoneStatus:read(mock_device):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) }) + test.socket.zigbee:__expect_send({ mock_device.id, PowerConfiguration.attributes.BatteryVoltage:read(mock_device):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) }) + test.socket.zigbee:__expect_send({ mock_device.id, TemperatureMeasurement.attributes.MeasuredValue:read(mock_device):to_endpoint(TEMPERATURE_ENDPOINT) }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.read_manufacturer_specific_attribute( + mock_device, + Frient_AccelerationMeasurementCluster.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, + Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.read_manufacturer_specific_attribute( + mock_device, + Frient_AccelerationMeasurementCluster.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, + Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.read_manufacturer_specific_attribute( + mock_device, + Frient_AccelerationMeasurementCluster.ID, + Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, + Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) +end +) + +test.register_message_test( + "Reported ZoneStatus change should be handled: active motion and inactive acceleration", + { + { + channel = "zigbee", + direction = "receive", + message = {mock_device.id, IASZone.attributes.ZoneStatus:build_test_attr_report(mock_device, 0x0001)} + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.motionSensor.motion.active(mock_device)) + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.accelerationSensor.acceleration.inactive(mock_device)) + } + } +) + +test.register_message_test( + "Reported ZoneStatus change should be handled: inactive motion and active acceleration", + { + { + channel = "zigbee", + direction = "receive", + message = {mock_device.id, IASZone.attributes.ZoneStatus:build_test_attr_report(mock_device, 0x0002)} + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.motionSensor.motion.inactive(mock_device)) + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.accelerationSensor.acceleration.active(mock_device)) + } + } +) + +test.register_coroutine_test( + "Three Axis report should be correctly handled", + function() + local attr_report_data = { + { 0x0000, data_types.Int16.ID, 300}, + { 0x0001, data_types.Int16.ID, 200}, + { 0x0002, data_types.Int16.ID, 100}, + } + test.socket.zigbee:__queue_receive({ + mock_device.id, + zigbee_test_utils.build_attribute_report(mock_device, Frient_AccelerationMeasurementCluster.ID, attr_report_data, 0x1015) + }) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + end +) + +test.run_registered_tests() \ No newline at end of file From fb7628b39c5541858f907d3cbabf3b8e52d7c362 Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Mon, 6 Oct 2025 13:50:53 +0200 Subject: [PATCH 02/13] garageDoor functionality added --- ...cceleration-motion-temperature-battery.yml | 13 ++- .../src/frient/frient-vibration/init.lua | 8 ++ .../src/test/test_frient_vibration_sensor.lua | 102 ++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml index 13506508c7..e65cdbe442 100644 --- a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml @@ -8,6 +8,8 @@ components: version: 1 - id: threeAxis version: 1 + - id: contactSensor + version: 1 - id: temperatureMeasurement version: 1 - id: battery @@ -38,4 +40,13 @@ preferences: definition: minimum: 1 maximum: 15 - default: 10 \ No newline at end of file + default: 10 + - title: "Use on garage door" + name: garageSensor + required: false + preferenceType: enumeration + definition: + options: + "Yes": "Yes" + "No": "No" + default: "No" \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index f6f7318159..54b2c52688 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -83,6 +83,14 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, if measured_x and measured_y and measured_z then device:emit_event(threeAxis.threeAxis({measured_x, measured_y, measured_z})) end + + if device.preferences.garageSensor == "Yes" then + if measured_z < -900 then + device:emit_event(capabilities.contactSensor.contact.open()) + elseif measured_z >= -100 then + device:emit_event(capabilities.contactSensor.contact.closed()) + end + end end local function get_cluster_configurations() diff --git a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua index c3745482fa..4b6ee3ec3c 100644 --- a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua +++ b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua @@ -117,6 +117,16 @@ test.register_coroutine_test( ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) }) + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request( + mock_device, + zigbee_test_utils.mock_hub_eui, + IASZone.ID, + POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + test.socket.zigbee:__expect_send({ mock_device.id, zigbee_test_utils.build_bind_request( @@ -127,6 +137,16 @@ test.register_coroutine_test( ):to_endpoint(TEMPERATURE_ENDPOINT) }) + test.socket.zigbee:__expect_send({ + mock_device.id, + IASZone.attributes.ZoneStatus:configure_reporting( + mock_device, + 0x001E, + 0x012C, + 1 + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) + }) + test.socket.zigbee:__expect_send({ mock_device.id, TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( @@ -423,4 +443,86 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Contact sensor open events should be correctly handled when preference is set", + function() + local updates = { + preferences = { + garageSensor = "Yes" + } + } + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__set_channel_ordering("relaxed") + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed(updates)) + local attr_report_data = { + { 0x0000, data_types.Int16.ID, 300}, + { 0x0001, data_types.Int16.ID, 200}, + { 0x0002, data_types.Int16.ID, -902}, + } + test.socket.zigbee:__queue_receive({ + mock_device.id, + zigbee_test_utils.build_attribute_report(mock_device, Frient_AccelerationMeasurementCluster.ID, attr_report_data, 0x1015) + }) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) + ) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) + ) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) + ) + + --if (mock_device.preferences.garageSensor == "Yes") then + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.contactSensor.contact.open()) + ) + --end + end +) + +test.register_coroutine_test( + "Contact sensor close events should be correctly handled when preference is set", + function() + local updates = { + preferences = { + garageSensor = "Yes" + } + } + test.socket.zigbee:__set_channel_ordering("relaxed") + test.socket.capability:__set_channel_ordering("relaxed") + test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed(updates)) + local attr_report_data = { + { 0x0000, data_types.Int16.ID, 300}, + { 0x0001, data_types.Int16.ID, 200}, + { 0x0002, data_types.Int16.ID, 100}, + } + test.socket.zigbee:__queue_receive({ + mock_device.id, + zigbee_test_utils.build_attribute_report(mock_device, Frient_AccelerationMeasurementCluster.ID, attr_report_data, 0x1015) + }) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + + --if (mock_device.preferences.garageSensor == "Yes") then + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.contactSensor.contact.closed()) + ) + --end + end +) + test.run_registered_tests() \ No newline at end of file From 3fc7be6318bc48eca4daa0b3dfd7e06808f474fb Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Tue, 7 Oct 2025 08:33:54 +0200 Subject: [PATCH 03/13] add profile with contact sensor capability --- ...cceleration-motion-temperature-battery.yml | 2 - ...ion-motion-temperature-contact-battery.yml | 52 +++++++++++++++++++ .../src/frient/frient-vibration/init.lua | 7 +++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml index e65cdbe442..9658f787fc 100644 --- a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml @@ -8,8 +8,6 @@ components: version: 1 - id: threeAxis version: 1 - - id: contactSensor - version: 1 - id: temperatureMeasurement version: 1 - id: battery diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml new file mode 100644 index 0000000000..0a94211284 --- /dev/null +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml @@ -0,0 +1,52 @@ +name: acceleration-motion-temperature-contact-battery +components: +- id: main + capabilities: + - id: accelerationSensor + version: 1 + - id: motionSensor + version: 1 + - id: threeAxis + version: 1 + - id: contactSensor + version: 1 + - id: temperatureMeasurement + version: 1 + - id: battery + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: VibrationsSensor +preferences: + - preferenceId: tempOffset + explicit: true + - title: "Temperature sensitivity (°)" + name: temperatureSensitivity + description: "Minimum change in temperature to report" + required: false + preferenceType: number + definition: + minimum: 0.1 + maximum: 2.0 + default: 1.0 + - title: "Sensitivity level" + name: sensitivityLevel + description: "How sensitivite the device is to vibrations" + required: false + preferenceType: integer + definition: + minimum: 1 + maximum: 15 + default: 10 + - title: "Use on garage door" + name: garageSensor + required: false + preferenceType: enumeration + definition: + options: + "Yes": "Yes" + "No": "No" + default: "No" \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index 54b2c52688..10ae17a0db 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -192,6 +192,13 @@ local function info_changed(driver, device, event, args) log.debug("Configuring temperature sensitivity: "..sensitivity) device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting(device, 30, 1*60*60, sensitivity):to_endpoint(0x26)) end + if args.old_st_store.preferences.garageSensor ~= device.preferences.garageSensor then + if device.preferences.garageSensor == "Yes" then + device:try_update_metadata({profile = "acceleration-motion-temperature-contact-battery"}) + elseif device.preferences.garageSensor == "No" then + device:try_update_metadata({profile = "acceleration-motion-temperature-battery"}) + end + end device.thread:call_with_delay(5, function() device:refresh() end) From 30e6c96e1ad1f93a6fb17ea598a0f9cf018f6129 Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Tue, 7 Oct 2025 09:06:13 +0200 Subject: [PATCH 04/13] changes to test file --- .../src/frient/frient-vibration/init.lua | 2 +- .../src/test/test_frient_vibration_sensor.lua | 42 ++++++++++++++----- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index 10ae17a0db..7964efb514 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -84,7 +84,7 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, device:emit_event(threeAxis.threeAxis({measured_x, measured_y, measured_z})) end - if device.preferences.garageSensor == "Yes" then + if device.supports_capability(capabilities.contactSensor) then if measured_z < -900 then device:emit_event(capabilities.contactSensor.contact.open()) elseif measured_z >= -100 then diff --git a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua index 4b6ee3ec3c..2b914374e6 100644 --- a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua +++ b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua @@ -50,6 +50,27 @@ local mock_device = test.mock_device.build_test_zigbee_device( } ) +local mock_device_contact = test.mock_device.build_test_zigbee_device( + { profile = t_utils.get_profile_definition("acceleration-motion-temperature-contact-battery.yml"), + zigbee_endpoints = { + [0x01] = { + id = 0x01, + manufacturer = "frient A/S", + model = "WISZB-137", + server_clusters = { 0x0003, 0x0005, 0x0006 } + }, + [0x2D] = { + id = 0x2D, + server_clusters = { 0x0000, 0x0001, 0x0003, 0x0020, 0x0500, 0xFC04 } + }, + [0x26] = { + id = 0x26, + server_clusters = { 0x0402 } + } + } + } +) + local Frient_AccelerationMeasurementCluster = { ID = 0xFC04, ManufacturerSpecificCode = 0x1015, @@ -64,6 +85,7 @@ zigbee_test_utils.prepare_zigbee_env_info() local function test_init() test.mock_device.add_test_device(mock_device) + test.mock_device.add_test_device(mock_device_contact) end test.set_test_init_function(test_init) @@ -453,34 +475,32 @@ test.register_coroutine_test( } test.socket.zigbee:__set_channel_ordering("relaxed") test.socket.capability:__set_channel_ordering("relaxed") - test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed(updates)) + test.socket.device_lifecycle:__queue_receive(mock_device_contact:generate_info_changed(updates)) local attr_report_data = { { 0x0000, data_types.Int16.ID, 300}, { 0x0001, data_types.Int16.ID, 200}, { 0x0002, data_types.Int16.ID, -902}, } test.socket.zigbee:__queue_receive({ - mock_device.id, - zigbee_test_utils.build_attribute_report(mock_device, Frient_AccelerationMeasurementCluster.ID, attr_report_data, 0x1015) + mock_device_contact.id, + zigbee_test_utils.build_attribute_report(mock_device_contact, Frient_AccelerationMeasurementCluster.ID, attr_report_data, 0x1015) }) test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) + mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) ) test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) + mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) ) test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) + mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) ) - --if (mock_device.preferences.garageSensor == "Yes") then - test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.contactSensor.contact.open()) - ) - --end + test.socket.capability:__expect_send( + mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.open()) + ) end ) From c0339821014d3d3a0d53819f644ab7382d5da29e Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Tue, 7 Oct 2025 14:54:11 +0200 Subject: [PATCH 05/13] tests and profile fixed --- ...ion-motion-temperature-contact-battery.yml | 2 +- .../src/frient/frient-vibration/init.lua | 2 +- .../src/test/test_frient_vibration_sensor.lua | 122 +++++++++--------- 3 files changed, 64 insertions(+), 62 deletions(-) diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml index 0a94211284..d7c25d3fb5 100644 --- a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml @@ -49,4 +49,4 @@ preferences: options: "Yes": "Yes" "No": "No" - default: "No" \ No newline at end of file + default: "Yes" \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index 7964efb514..10ae17a0db 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -84,7 +84,7 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, device:emit_event(threeAxis.threeAxis({measured_x, measured_y, measured_z})) end - if device.supports_capability(capabilities.contactSensor) then + if device.preferences.garageSensor == "Yes" then if measured_z < -900 then device:emit_event(capabilities.contactSensor.contact.open()) elseif measured_z >= -100 then diff --git a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua index 2b914374e6..f280cf96d7 100644 --- a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua +++ b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua @@ -112,17 +112,17 @@ test.register_coroutine_test( test.socket.environment_update:__queue_receive({ "zigbee", { hub_zigbee_id = base64.encode(zigbee_test_utils.mock_hub_eui) } }) test.socket.zigbee:__set_channel_ordering("relaxed") - test.socket.device_lifecycle:__queue_receive({ mock_device.id, "init" }) + test.socket.device_lifecycle:__queue_receive({ mock_device_contact.id, "init" }) test.wait_for_events() --test.socket.device_lifecycle:__queue_receive({ mock_device.id, "added" }) - test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" }) + test.socket.device_lifecycle:__queue_receive({ mock_device_contact.id, "doConfigure" }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, zigbee_test_utils.build_bind_request( - mock_device, + mock_device_contact, zigbee_test_utils.mock_hub_eui, PowerConfiguration.ID, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT @@ -130,9 +130,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, PowerConfiguration.attributes.BatteryVoltage:configure_reporting( - mock_device, + mock_device_contact, 30, 21600, 1 @@ -140,9 +140,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, zigbee_test_utils.build_bind_request( - mock_device, + mock_device_contact, zigbee_test_utils.mock_hub_eui, IASZone.ID, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT @@ -150,9 +150,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, zigbee_test_utils.build_bind_request( - mock_device, + mock_device_contact, zigbee_test_utils.mock_hub_eui, TemperatureMeasurement.ID, TEMPERATURE_ENDPOINT @@ -160,9 +160,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, IASZone.attributes.ZoneStatus:configure_reporting( - mock_device, + mock_device_contact, 0x001E, 0x012C, 1 @@ -170,9 +170,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( - mock_device, + mock_device_contact, 30, 600, 100 @@ -180,17 +180,17 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, IASZone.attributes.IASCIEAddress:write( - mock_device, + mock_device_contact, zigbee_test_utils.mock_hub_eui ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, zigbee_test_utils.build_bind_request( - mock_device, + mock_device_contact, zigbee_test_utils.mock_hub_eui, Frient_AccelerationMeasurementCluster.ID, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT @@ -198,9 +198,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, custom_configure_reporting( - mock_device, + mock_device_contact, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.data_type, @@ -212,9 +212,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, custom_configure_reporting( - mock_device, + mock_device_contact, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.data_type, @@ -226,9 +226,9 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, custom_configure_reporting( - mock_device, + mock_device_contact, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.data_type, @@ -240,18 +240,18 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, IASZone.server.commands.ZoneEnrollResponse( - mock_device, + mock_device_contact, IasEnrollResponseCode.SUCCESS, 0x00 ) }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, zigbee_test_utils.build_bind_request( - mock_device, + mock_device_contact, zigbee_test_utils.mock_hub_eui, IASZone.ID, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT @@ -260,17 +260,17 @@ test.register_coroutine_test( test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, IASZone.attributes.CurrentZoneSensitivityLevel:write( - mock_device, + mock_device_contact, 0x000A ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, TemperatureMeasurement.attributes.MeasuredValue:configure_reporting( - mock_device, + mock_device_contact, 0x001E, 0x0E10, 100 @@ -278,26 +278,26 @@ test.register_coroutine_test( }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, IASZone.attributes.ZoneStatus:configure_reporting( - mock_device, + mock_device_contact, 0, 3600, 0 - ) + ):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT) }) test.socket.zigbee:__expect_send({ - mock_device.id, + mock_device_contact.id, zigbee_test_utils.build_bind_request( - mock_device, + mock_device_contact, zigbee_test_utils.mock_hub_eui, Frient_AccelerationMeasurementCluster.ID, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT ) }) - mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + mock_device_contact:expect_metadata_update({ provisioning_state = "PROVISIONED" }) end ) @@ -468,14 +468,8 @@ test.register_coroutine_test( test.register_coroutine_test( "Contact sensor open events should be correctly handled when preference is set", function() - local updates = { - preferences = { - garageSensor = "Yes" - } - } test.socket.zigbee:__set_channel_ordering("relaxed") test.socket.capability:__set_channel_ordering("relaxed") - test.socket.device_lifecycle:__queue_receive(mock_device_contact:generate_info_changed(updates)) local attr_report_data = { { 0x0000, data_types.Int16.ID, 300}, { 0x0001, data_types.Int16.ID, 200}, @@ -490,10 +484,18 @@ test.register_coroutine_test( mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) ) + test.socket.capability:__expect_send( + mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.open()) + ) + test.socket.capability:__expect_send( mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) ) + test.socket.capability:__expect_send( + mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.open()) + ) + test.socket.capability:__expect_send( mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) ) @@ -507,41 +509,41 @@ test.register_coroutine_test( test.register_coroutine_test( "Contact sensor close events should be correctly handled when preference is set", function() - local updates = { - preferences = { - garageSensor = "Yes" - } - } test.socket.zigbee:__set_channel_ordering("relaxed") test.socket.capability:__set_channel_ordering("relaxed") - test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed(updates)) local attr_report_data = { { 0x0000, data_types.Int16.ID, 300}, { 0x0001, data_types.Int16.ID, 200}, { 0x0002, data_types.Int16.ID, 100}, } test.socket.zigbee:__queue_receive({ - mock_device.id, - zigbee_test_utils.build_attribute_report(mock_device, Frient_AccelerationMeasurementCluster.ID, attr_report_data, 0x1015) + mock_device_contact.id, + zigbee_test_utils.build_attribute_report(mock_device_contact, Frient_AccelerationMeasurementCluster.ID, attr_report_data, 0x1015) }) test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) ) test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.closed()) ) test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + + test.socket.capability:__expect_send( + mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.closed()) ) - --if (mock_device.preferences.garageSensor == "Yes") then - test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.contactSensor.contact.closed()) - ) - --end + test.socket.capability:__expect_send( + mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) + ) + + test.socket.capability:__expect_send( + mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.closed()) + ) end ) From 230a497192bc3b82dc41eefba56af2cba85c9f8a Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Tue, 14 Oct 2025 12:02:34 +0200 Subject: [PATCH 06/13] Add tolerance to measurement --- ...ion-motion-temperature-contact-battery.yml | 41 ++++++++++++++++++- .../src/frient/frient-vibration/init.lua | 24 ++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml index d7c25d3fb5..b6d8ed3f5c 100644 --- a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml @@ -41,7 +41,7 @@ preferences: minimum: 1 maximum: 15 default: 10 - - title: "Use on garage door" + - title: "Use with Contact Sensor" name: garageSensor required: false preferenceType: enumeration @@ -49,4 +49,41 @@ preferences: options: "Yes": "Yes" "No": "No" - default: "Yes" \ No newline at end of file + default: "Yes" + - title: "Axis to activate Contact Sensor" + name: contactSensorAxis + required: false + preferenceType: enumeration + definition: + options: + "X": "X" + "Y": "Y" + "Z": "Z" + default: "Z" + - title: "Initial position (closed state)" + name: sensorInitialPosition + description: "Initial position of the device in the chosen axis" + required: false + preferenceType: number + definition: + minimum: -2000 + maximum: 2000 + default: 0 + - title: "Contact Sensor threshold (open)" + name: contactSensorValue + description: "Value change required to trigger contact sensor" + required: false + preferenceType: number + definition: + minimum: 20 + maximum: 4000 + default: 900 + - title: "Measurement tolerance" + name: tolerance + description: "Set the tolerance in percentage of the threshold" + required: false + preferenceType: number + definition: + minimum: 0 + maximum: 20 + default: 0 \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index 10ae17a0db..97ae6d3ecd 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -85,10 +85,30 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, end if device.preferences.garageSensor == "Yes" then - if measured_z < -900 then + if device.preferences.contactSensorAxis == "X" then + local initial_position = device.preferences.sensorInitialPosition or 0 + log.debug("Difference X: " .. math.abs(initial_position - measured_x) .. " Threshold: " .. device.preferences.contactSensorValue) + if math.abs(initial_position - measured_x) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then device:emit_event(capabilities.contactSensor.contact.open()) - elseif measured_z >= -100 then + else device:emit_event(capabilities.contactSensor.contact.closed()) + end + elseif device.preferences.contactSensorAxis == "Y" then + local initial_position = device.preferences.sensorInitialPosition or 0 + log.debug("Difference Y: " .. math.abs(initial_position - measured_y) .. " Threshold: " .. device.preferences.contactSensorValue) + if math.abs(initial_position - measured_y) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then + device:emit_event(capabilities.contactSensor.contact.open()) + else + device:emit_event(capabilities.contactSensor.contact.closed()) + end + elseif device.preferences.contactSensorAxis == "Z" then + local initial_position = device.preferences.sensorInitialPosition or 0 + log.debug("Difference Z: " .. math.abs(initial_position - measured_z) .. " Threshold: " .. device.preferences.contactSensorValue) + if math.abs(initial_position - measured_z) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then + device:emit_event(capabilities.contactSensor.contact.open()) + else + device:emit_event(capabilities.contactSensor.contact.closed()) + end end end end From 34c2d926314279c64f58c69a736bb8310e205e5f Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Tue, 14 Oct 2025 13:20:05 +0200 Subject: [PATCH 07/13] fix failing tests --- drivers/SmartThings/zigbee-contact/fingerprints.yml | 5 +++++ drivers/SmartThings/zigbee-contact/src/configurations.lua | 1 + drivers/SmartThings/zigbee-contact/src/frient/init.lua | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-contact/fingerprints.yml b/drivers/SmartThings/zigbee-contact/fingerprints.yml index ff3faed09b..c30c3296a2 100644 --- a/drivers/SmartThings/zigbee-contact/fingerprints.yml +++ b/drivers/SmartThings/zigbee-contact/fingerprints.yml @@ -149,6 +149,11 @@ zigbeeManufacturer: manufacturer: frient A/S model: WISZB-121 deviceProfileName: contact-battery-profile + - id: "frient A/S/WISZB-131" + deviceLabel: frient Entry Sensor 2 Pro + manufacturer: frient A/S + model: WISZB-131 + deviceProfileName: frient-contact-battery-temperature - id: "frient A/S/WISZB-137" deviceLabel: frient Vibration Sensor manufacturer: frient A/S diff --git a/drivers/SmartThings/zigbee-contact/src/configurations.lua b/drivers/SmartThings/zigbee-contact/src/configurations.lua index d998335062..1e08ecffd4 100644 --- a/drivers/SmartThings/zigbee-contact/src/configurations.lua +++ b/drivers/SmartThings/zigbee-contact/src/configurations.lua @@ -92,6 +92,7 @@ local devices = { { mfr = "Sercomm Corp.", model = "SZ-DWS04" }, { mfr = "DAWON_DNS", model = "SS-B100-ZB" }, { mfr = "frient A/S", model = "WISZB-120" }, + { mfr = "frient A/S", model = "WISZB-131" }, { mfr = "Compacta", model = "ZBWDS" } }, CONFIGURATION = { diff --git a/drivers/SmartThings/zigbee-contact/src/frient/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/init.lua index 33f362ca80..1379d0a8c0 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/init.lua @@ -96,7 +96,7 @@ local frient_sensor = { } }, can_handle = function(opts, driver, device, ...) - return (device:get_manufacturer() == "frient A/S") and (device:get_model() == "WISZB-120" or device:get_model() == "WISZB-121" or device:get_model() == "WISZB-137") + return (device:get_manufacturer() == "frient A/S") and (device:get_model() == "WISZB-120" or device:get_model() == "WISZB-121" or device:get_model() == "WISZB-131" or device:get_model() == "WISZB-137") end } From 54b578c32fb2caaf6121015079a7dac409e6cf55 Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Tue, 14 Oct 2025 13:36:22 +0200 Subject: [PATCH 08/13] change preference name --- .../profiles/acceleration-motion-temperature-battery.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml index 9658f787fc..b4477199aa 100644 --- a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml @@ -39,7 +39,7 @@ preferences: minimum: 1 maximum: 15 default: 10 - - title: "Use on garage door" + - title: "Use with Contact Sensor" name: garageSensor required: false preferenceType: enumeration From 9841cf235aa801ea1df38f0cdde1093925f85b4c Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Mon, 17 Nov 2025 08:41:58 +0100 Subject: [PATCH 09/13] removed logging, changed data_type and fixed indentation --- .../src/frient/frient-vibration/init.lua | 123 ++++++++---------- 1 file changed, 52 insertions(+), 71 deletions(-) diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index 97ae6d3ecd..c41e507b7c 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -18,8 +18,6 @@ local cluster_base = require "st.zigbee.cluster_base" local battery_defaults = require "st.zigbee.defaults.battery_defaults" local device_management = require "st.zigbee.device_management" local data_types = require "st.zigbee.data_types" -local log = require "log" -local util = require "st.utils" local threeAxis = capabilities.threeAxis local TemperatureMeasurement = zcl_clusters.TemperatureMeasurement @@ -45,9 +43,9 @@ local Frient_AccelerationMeasurementCluster = { ID = 0xFC04, ManufacturerSpecificCode = 0x1015, attributes = { - MeasuredValueX = { ID = 0x0000, data_type = data_types.name_to_id_map["Int16"] }, - MeasuredValueY = { ID = 0x0001, data_type = data_types.name_to_id_map["Int16"] }, - MeasuredValueZ = { ID = 0x0002, data_type = data_types.name_to_id_map["Int16"] } + MeasuredValueX = { ID = 0x0000, data_type = data_types.Int16 }, + MeasuredValueY = { ID = 0x0001, data_type = data_types.Int16 }, + MeasuredValueZ = { ID = 0x0002, data_type = data_types.Int16 } }, } @@ -65,17 +63,12 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, if attribute_id == 0x0000 then measured_x = axis_value device:set_field("measured_x", measured_x) - log.trace("Updated X value: " .. axis_value) elseif attribute_id == 0x0001 then measured_y = axis_value device:set_field("measured_y", measured_y) - log.trace("Updated Y value: " .. axis_value) elseif attribute_id == 0x0002 then measured_z = axis_value device:set_field("measured_z", measured_z) - log.trace("Updated Z value: " .. axis_value) - else - log.warn("Unknown AttributeId: " .. tostring(attribute_id)) end end @@ -87,7 +80,6 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, if device.preferences.garageSensor == "Yes" then if device.preferences.contactSensorAxis == "X" then local initial_position = device.preferences.sensorInitialPosition or 0 - log.debug("Difference X: " .. math.abs(initial_position - measured_x) .. " Threshold: " .. device.preferences.contactSensorValue) if math.abs(initial_position - measured_x) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then device:emit_event(capabilities.contactSensor.contact.open()) else @@ -95,7 +87,6 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, end elseif device.preferences.contactSensorAxis == "Y" then local initial_position = device.preferences.sensorInitialPosition or 0 - log.debug("Difference Y: " .. math.abs(initial_position - measured_y) .. " Threshold: " .. device.preferences.contactSensorValue) if math.abs(initial_position - measured_y) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then device:emit_event(capabilities.contactSensor.contact.open()) else @@ -103,7 +94,6 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, end elseif device.preferences.contactSensorAxis == "Z" then local initial_position = device.preferences.sensorInitialPosition or 0 - log.debug("Difference Z: " .. math.abs(initial_position - measured_z) .. " Threshold: " .. device.preferences.contactSensorValue) if math.abs(initial_position - measured_z) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then device:emit_event(capabilities.contactSensor.contact.open()) else @@ -115,33 +105,33 @@ end local function get_cluster_configurations() return { - { - cluster = Frient_AccelerationMeasurementCluster.ID, - attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, - minimum_interval = 0, - maximum_interval = 300, - reportable_change = 0x0001, - data_type = data_types.Int16, - mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode - }, - { - cluster = Frient_AccelerationMeasurementCluster.ID, - attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, - minimum_interval = 0, - maximum_interval = 300, - reportable_change = 0x0001, - data_type = data_types.Int16, - mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode - }, - { - cluster = Frient_AccelerationMeasurementCluster.ID, - attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, - minimum_interval = 0, - maximum_interval = 300, - reportable_change = 0x0001, - data_type = data_types.Int16, - mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode - } + { + cluster = Frient_AccelerationMeasurementCluster.ID, + attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, + minimum_interval = 0, + maximum_interval = 300, + reportable_change = 0x0001, + data_type = data_types.Int16, + mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + }, + { + cluster = Frient_AccelerationMeasurementCluster.ID, + attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, + minimum_interval = 0, + maximum_interval = 300, + reportable_change = 0x0001, + data_type = data_types.Int16, + mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + }, + { + cluster = Frient_AccelerationMeasurementCluster.ID, + attribute = Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, + minimum_interval = 0, + maximum_interval = 300, + reportable_change = 0x0001, + data_type = data_types.Int16, + mfg_code = Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode + } } end @@ -151,65 +141,56 @@ local function generate_event_from_zone_status(driver, device, zone_status, zb_r end local function ias_zone_status_attr_handler(driver, device, attr_val, zb_rx) - log.debug("Received IAS Zone Status:"..util.stringify_table(attr_val)) generate_event_from_zone_status(driver, device, attr_val, zb_rx) end local function ias_zone_status_change_handler(driver, device, zb_rx) - log.debug( "Received IAS Zone Change:"..util.stringify_table(zb_rx.body.zcl_body.zone_status)) generate_event_from_zone_status(driver, device, zb_rx.body.zcl_body.zone_status, zb_rx) end local function device_init(driver, device) - log.trace "Initializing sensor" - battery_defaults.build_linear_voltage_init(2.3, 3.0)(driver, device) - --Add the manufacturer-specific attributes to generate their configure reporting and bind requests - for _, config in pairs(get_cluster_configurations()) do - device:add_configured_attribute(config) - end + battery_defaults.build_linear_voltage_init(2.3, 3.0)(driver, device) + --Add the manufacturer-specific attributes to generate their configure reporting and bind requests + for _, config in pairs(get_cluster_configurations()) do + device:add_configured_attribute(config) + end end local function do_refresh(driver, device) - log.trace "Refreshing vibration sensor attributes" - device:send(IASZone.attributes.ZoneStatus:read(device):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device):to_endpoint(TEMPERATURE_ENDPOINT)) - device:send(PowerConfiguration.attributes.BatteryVoltage:read(device)) + device:send(IASZone.attributes.ZoneStatus:read(device):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(cluster_base.read_manufacturer_specific_attribute(device, Frient_AccelerationMeasurementCluster.ID, Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID, Frient_AccelerationMeasurementCluster.ManufacturerSpecificCode):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device):to_endpoint(TEMPERATURE_ENDPOINT)) + device:send(PowerConfiguration.attributes.BatteryVoltage:read(device)) end local function do_configure(driver, device, event, args) - log.trace("Configuring sensor:" .. event) - device:configure() + device:configure() - device:send(device_management.build_bind_request(device, zcl_clusters.IASZone.ID, driver.environment_info.hub_zigbee_eui, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - device:send(IASZone.attributes.ZoneStatus:configure_reporting(device, 0, 1*60*60, 1):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - device:send(device_management.build_bind_request(device, Frient_AccelerationMeasurementCluster.ID, driver.environment_info.hub_zigbee_eui, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(device_management.build_bind_request(device, zcl_clusters.IASZone.ID, driver.environment_info.hub_zigbee_eui, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(IASZone.attributes.ZoneStatus:configure_reporting(device, 0, 1*60*60, 1):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + device:send(device_management.build_bind_request(device, Frient_AccelerationMeasurementCluster.ID, driver.environment_info.hub_zigbee_eui, POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - local sensitivityLevel = device.preferences.sensitivityLevel or 10 - log.debug("Writing CurrentZoneSensitivityLevel attribute to: " .. sensitivityLevel) - device:send(IASZone.attributes.CurrentZoneSensitivityLevel:write(device, sensitivityLevel):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) + local sensitivityLevel = device.preferences.sensitivityLevel or 10 + device:send(IASZone.attributes.CurrentZoneSensitivityLevel:write(device, sensitivityLevel):to_endpoint(POWER_CONFIGURATION_AND_ACCELERATION_ENDPOINT)) - local sensitivity = math.floor((device.preferences.temperatureSensitivity or 0.1) * 100 + 0.5) - log.debug("Configuring temperature sensitivity: " .. sensitivity) - device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting(device, 30, 1 * 60 * 60, sensitivity):to_endpoint(TEMPERATURE_ENDPOINT)) + local sensitivity = math.floor((device.preferences.temperatureSensitivity or 0.1) * 100 + 0.5) + device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting(device, 30, 1 * 60 * 60, sensitivity):to_endpoint(TEMPERATURE_ENDPOINT)) - device.thread:call_with_delay(5, function() - device:refresh() - end) + device.thread:call_with_delay(5, function() + device:refresh() + end) end local function info_changed(driver, device, event, args) if args and args.old_st_store then if args.old_st_store.preferences.sensitivityLevel ~= device.preferences.sensitivityLevel then local sensitivityLevel = device.preferences.sensitivityLevel or 10 - log.debug("Writing Current Zone Sensitivity Level Attribute To: "..sensitivityLevel) device:send(IASZone.attributes.CurrentZoneSensitivityLevel:write(device, sensitivityLevel):to_endpoint(0x2D)) end if args.old_st_store.preferences.temperatureSensitivity ~= device.preferences.temperatureSensitivity then local sensitivity = math.floor((device.preferences.temperatureSensitivity or 0.1)*100 + 0.5) - log.debug("Configuring temperature sensitivity: "..sensitivity) device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting(device, 30, 1*60*60, sensitivity):to_endpoint(0x26)) end if args.old_st_store.preferences.garageSensor ~= device.preferences.garageSensor then From 1609b3e8bd6a6bb2eb53ea2bbb7ec17386f3dbda Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Mon, 24 Nov 2025 08:23:01 +0100 Subject: [PATCH 10/13] suggested refactoring --- .../src/frient/frient-vibration/init.lua | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index c41e507b7c..44f52ba750 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -78,27 +78,17 @@ local function acceleration_measure_value_attr_handler(driver, device, attr_val, end if device.preferences.garageSensor == "Yes" then - if device.preferences.contactSensorAxis == "X" then - local initial_position = device.preferences.sensorInitialPosition or 0 - if math.abs(initial_position - measured_x) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then - device:emit_event(capabilities.contactSensor.contact.open()) - else - device:emit_event(capabilities.contactSensor.contact.closed()) - end - elseif device.preferences.contactSensorAxis == "Y" then - local initial_position = device.preferences.sensorInitialPosition or 0 - if math.abs(initial_position - measured_y) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then - device:emit_event(capabilities.contactSensor.contact.open()) - else - device:emit_event(capabilities.contactSensor.contact.closed()) - end + local garageAxis = measured_x + if device.preferences.contactSensorAxis == "Y" then + garageAxis = measured_y elseif device.preferences.contactSensorAxis == "Z" then - local initial_position = device.preferences.sensorInitialPosition or 0 - if math.abs(initial_position - measured_z) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then - device:emit_event(capabilities.contactSensor.contact.open()) - else - device:emit_event(capabilities.contactSensor.contact.closed()) - end + garageAxis = measured_z + end + local initial_position = device.preferences.sensorInitialPosition or 0 + if math.abs(initial_position - garageAxis) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then + device:emit_event(capabilities.contactSensor.contact.open()) + else + device:emit_event(capabilities.contactSensor.contact.closed()) end end end From ca4ce270e8f2034ff2979ac44a2ff7f87b719e8f Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Mon, 2 Feb 2026 12:56:12 +0100 Subject: [PATCH 11/13] update driver so it sends only one threeAxis event --- .../src/frient/frient-vibration/init.lua | 49 +++++++++---------- .../src/test/test_frient_vibration_sensor.lua | 40 --------------- 2 files changed, 24 insertions(+), 65 deletions(-) diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index 44f52ba750..5273c34809 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -13,6 +13,7 @@ -- limitations under the License. local capabilities = require "st.capabilities" +local zcl_commands = require "st.zigbee.zcl.global_commands" local zcl_clusters = require "st.zigbee.zcl.clusters" local cluster_base = require "st.zigbee.cluster_base" local battery_defaults = require "st.zigbee.defaults.battery_defaults" @@ -49,46 +50,43 @@ local Frient_AccelerationMeasurementCluster = { }, } -local function acceleration_measure_value_attr_handler(driver, device, attr_val, zb_rx) - -- Initialize variables to store the axis values +local function acceleration_measure_report_handler(driver, device, zb_rx) local measured_x = device:get_field("measured_x") local measured_y = device:get_field("measured_y") local measured_z = device:get_field("measured_z") - -- Process the attribute records for _, attribute_record in ipairs(zb_rx.body.zcl_body.attr_records) do local attribute_id = attribute_record.attr_id.value local axis_value = attribute_record.data.value - if attribute_id == 0x0000 then + if attribute_id == Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID then measured_x = axis_value device:set_field("measured_x", measured_x) - elseif attribute_id == 0x0001 then + elseif attribute_id == Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID then measured_y = axis_value device:set_field("measured_y", measured_y) - elseif attribute_id == 0x0002 then + elseif attribute_id == Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID then measured_z = axis_value device:set_field("measured_z", measured_z) end end - -- Ensure all values are non-nil before emitting if measured_x and measured_y and measured_z then device:emit_event(threeAxis.threeAxis({measured_x, measured_y, measured_z})) - end - if device.preferences.garageSensor == "Yes" then - local garageAxis = measured_x - if device.preferences.contactSensorAxis == "Y" then - garageAxis = measured_y - elseif device.preferences.contactSensorAxis == "Z" then - garageAxis = measured_z - end - local initial_position = device.preferences.sensorInitialPosition or 0 - if math.abs(initial_position - garageAxis) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then - device:emit_event(capabilities.contactSensor.contact.open()) - else - device:emit_event(capabilities.contactSensor.contact.closed()) + if device.preferences.garageSensor == "Yes" then + local garageAxis = measured_x + if device.preferences.contactSensorAxis == "Y" then + garageAxis = measured_y + elseif device.preferences.contactSensorAxis == "Z" then + garageAxis = measured_z + end + local initial_position = device.preferences.sensorInitialPosition or 0 + if math.abs(initial_position - garageAxis) >= device.preferences.contactSensorValue - device.preferences.contactSensorValue * (device.preferences.tolerance / 100) then + device:emit_event(capabilities.contactSensor.contact.open()) + else + device:emit_event(capabilities.contactSensor.contact.closed()) + end end end end @@ -204,6 +202,12 @@ local frient_vibration_driver_template = { infoChanged = info_changed }, zigbee_handlers = { + global = { + [Frient_AccelerationMeasurementCluster.ID] = { + [zcl_commands.ReportAttribute.ID] = acceleration_measure_report_handler, + [zcl_commands.ReadAttributeResponse.ID] = acceleration_measure_report_handler + } + }, cluster = { [IASZone.ID] = { [IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler @@ -212,11 +216,6 @@ local frient_vibration_driver_template = { attr = { [IASZone.ID] = { [IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler - }, - [Frient_AccelerationMeasurementCluster.ID] = { - [Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID] = acceleration_measure_value_attr_handler, - [Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID] = acceleration_measure_value_attr_handler, - [Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID] = acceleration_measure_value_attr_handler, } } }, diff --git a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua index f280cf96d7..2e704a61ca 100644 --- a/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua +++ b/drivers/SmartThings/zigbee-contact/src/test/test_frient_vibration_sensor.lua @@ -454,14 +454,6 @@ test.register_coroutine_test( test.socket.capability:__expect_send( mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) ) - - test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) - ) - - test.socket.capability:__expect_send( - mock_device:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) - ) end ) @@ -487,22 +479,6 @@ test.register_coroutine_test( test.socket.capability:__expect_send( mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.open()) ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) - ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.open()) - ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, -902})) - ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.open()) - ) end ) @@ -528,22 +504,6 @@ test.register_coroutine_test( test.socket.capability:__expect_send( mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.closed()) ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) - ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.closed()) - ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.threeAxis.threeAxis({300, 200, 100})) - ) - - test.socket.capability:__expect_send( - mock_device_contact:generate_test_message("main", capabilities.contactSensor.contact.closed()) - ) end ) From 784851f5a889ec262164fed9e2811f8748219ce5 Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Thu, 5 Feb 2026 09:17:57 +0100 Subject: [PATCH 12/13] Changes according to pr comments --- .../acceleration-motion-temperature-battery.yml | 2 +- ...acceleration-motion-temperature-contact-battery.yml | 2 +- .../src/frient/frient-vibration/init.lua | 10 +++------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml index b4477199aa..4f90f8a11e 100644 --- a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-battery.yml @@ -17,7 +17,7 @@ components: - id: refresh version: 1 categories: - - name: VibrationsSensor + - name: ContactSensor preferences: - preferenceId: tempOffset explicit: true diff --git a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml index b6d8ed3f5c..c40eb49b9e 100644 --- a/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml +++ b/drivers/SmartThings/zigbee-contact/profiles/acceleration-motion-temperature-contact-battery.yml @@ -19,7 +19,7 @@ components: - id: refresh version: 1 categories: - - name: VibrationsSensor + - name: ContactSensor preferences: - preferenceId: tempOffset explicit: true diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index 5273c34809..d44db00c26 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -20,6 +20,7 @@ local battery_defaults = require "st.zigbee.defaults.battery_defaults" local device_management = require "st.zigbee.device_management" local data_types = require "st.zigbee.data_types" local threeAxis = capabilities.threeAxis +local log = require "log" local TemperatureMeasurement = zcl_clusters.TemperatureMeasurement local IASZone = zcl_clusters.IASZone @@ -51,9 +52,7 @@ local Frient_AccelerationMeasurementCluster = { } local function acceleration_measure_report_handler(driver, device, zb_rx) - local measured_x = device:get_field("measured_x") - local measured_y = device:get_field("measured_y") - local measured_z = device:get_field("measured_z") + local measured_x, measured_y, measured_z for _, attribute_record in ipairs(zb_rx.body.zcl_body.attr_records) do local attribute_id = attribute_record.attr_id.value @@ -61,20 +60,17 @@ local function acceleration_measure_report_handler(driver, device, zb_rx) if attribute_id == Frient_AccelerationMeasurementCluster.attributes.MeasuredValueX.ID then measured_x = axis_value - device:set_field("measured_x", measured_x) elseif attribute_id == Frient_AccelerationMeasurementCluster.attributes.MeasuredValueY.ID then measured_y = axis_value - device:set_field("measured_y", measured_y) elseif attribute_id == Frient_AccelerationMeasurementCluster.attributes.MeasuredValueZ.ID then measured_z = axis_value - device:set_field("measured_z", measured_z) end end if measured_x and measured_y and measured_z then device:emit_event(threeAxis.threeAxis({measured_x, measured_y, measured_z})) - if device.preferences.garageSensor == "Yes" then + if device:supports_capability(capabilities.contactSensor) then local garageAxis = measured_x if device.preferences.contactSensorAxis == "Y" then garageAxis = measured_y From 9ff61c42c7ddad3fc4604cd3ff8b42bd5e2a59d2 Mon Sep 17 00:00:00 2001 From: Marcin Tyminski Date: Thu, 5 Feb 2026 09:18:50 +0100 Subject: [PATCH 13/13] removed unused variable --- .../zigbee-contact/src/frient/frient-vibration/init.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua index d44db00c26..2514bfb63b 100644 --- a/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua +++ b/drivers/SmartThings/zigbee-contact/src/frient/frient-vibration/init.lua @@ -20,7 +20,6 @@ local battery_defaults = require "st.zigbee.defaults.battery_defaults" local device_management = require "st.zigbee.device_management" local data_types = require "st.zigbee.data_types" local threeAxis = capabilities.threeAxis -local log = require "log" local TemperatureMeasurement = zcl_clusters.TemperatureMeasurement local IASZone = zcl_clusters.IASZone