diff --git a/drivers/SmartThings/zigbee-power-meter/fingerprints.yml b/drivers/SmartThings/zigbee-power-meter/fingerprints.yml index 37cf9df678..ec58259dd5 100644 --- a/drivers/SmartThings/zigbee-power-meter/fingerprints.yml +++ b/drivers/SmartThings/zigbee-power-meter/fingerprints.yml @@ -28,6 +28,46 @@ zigbeeManufacturer: manufacturer: ShinaSystem model: "PMM-300Z3" deviceProfileName: power-meter-consumption-report-sihas + - id: "BITUO TECHNIK/SPM01-E0" + deviceLabel: Energy Monitor 1PN + manufacturer: BITUO TECHNIK + model: "SPM01-E0" + deviceProfileName: power-meter-1p + - id: "BITUO TECHNIK/SPM01X" + deviceLabel: Energy Monitor 1PN + manufacturer: BITUO TECHNIK + model: "SPM01X" + deviceProfileName: power-meter-1p + - id: "BITUO TECHNIK/SDM02-E0" + deviceLabel: Energy Monitor 2PN + manufacturer: BITUO TECHNIK + model: "SDM02-E0" + deviceProfileName: power-meter-2p + - id: "BITUO TECHNIK/SDM02X" + deviceLabel: Energy Monitor 2PN + manufacturer: BITUO TECHNIK + model: "SDM02X" + deviceProfileName: power-meter-2p + - id: "BITUO TECHNIK/SPM02-E0" + deviceLabel: Energy Monitor 3PN + manufacturer: BITUO TECHNIK + model: "SPM02-E0" + deviceProfileName: power-meter-3p + - id: "BITUO TECHNIK/SPM02X" + deviceLabel: Energy Monitor 3PN + manufacturer: BITUO TECHNIK + model: "SPM02X" + deviceProfileName: power-meter-3p + - id: "BITUO TECHNIK/SDM01W" + deviceLabel: Energy Monitor 3PN + manufacturer: BITUO TECHNIK + model: "SDM01W" + deviceProfileName: power-meter-3p + - id: "BITUO TECHNIK/SDM01B" + deviceLabel: Energy Monitor 1PN + manufacturer: BITUO TECHNIK + model: "SDM01B" + deviceProfileName: power-meter-1p zigbeeGeneric: - id: "genericMeter" deviceLabel: Zigbee Meter diff --git a/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-1p.yml b/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-1p.yml new file mode 100644 index 0000000000..7290794ffe --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-1p.yml @@ -0,0 +1,31 @@ +name: power-meter-1p +components: +- id: main + label: Total Forward Energy + capabilities: + - id: energyMeter + version: 1 + - id: powerConsumptionReport + version: 1 + - id: refresh + version: 1 + categories: + - name: CurbPowerMeter +- id: TotalReverseEnergy + label: Total Reverse Energy + capabilities: + - id: energyMeter + version: 1 + categories: + - name: CurbPowerMeter +- id: PhaseA + label: Phase A + capabilities: + - id: powerMeter + version: 1 + - id: currentMeasurement + version: 1 + - id: voltageMeasurement + version: 1 + categories: + - name: CurbPowerMeter \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-2p.yml b/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-2p.yml new file mode 100644 index 0000000000..11f2dd412b --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-2p.yml @@ -0,0 +1,42 @@ +name: power-meter-2p +components: +- id: main + label: Total Forward Energy + capabilities: + - id: energyMeter + version: 1 + - id: powerConsumptionReport + version: 1 + - id: refresh + version: 1 + categories: + - name: CurbPowerMeter +- id: TotalReverseEnergy + label: Total Reverse Energy + capabilities: + - id: energyMeter + version: 1 + categories: + - name: CurbPowerMeter +- id: PhaseA + label: Phase A + capabilities: + - id: powerMeter + version: 1 + - id: currentMeasurement + version: 1 + - id: voltageMeasurement + version: 1 + categories: + - name: CurbPowerMeter +- id: PhaseB + label: Phase B + capabilities: + - id: powerMeter + version: 1 + - id: currentMeasurement + version: 1 + - id: voltageMeasurement + version: 1 + categories: + - name: CurbPowerMeter \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-3p.yml b/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-3p.yml new file mode 100644 index 0000000000..20c824d092 --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/profiles/power-meter-3p.yml @@ -0,0 +1,53 @@ +name: power-meter-3p +components: +- id: main + label: Total Forward Energy + capabilities: + - id: energyMeter + version: 1 + - id: powerConsumptionReport + version: 1 + - id: refresh + version: 1 + categories: + - name: CurbPowerMeter +- id: TotalReverseEnergy + label: Total Reverse Energy + capabilities: + - id: energyMeter + version: 1 + categories: + - name: CurbPowerMeter +- id: PhaseA + label: Phase A + capabilities: + - id: powerMeter + version: 1 + - id: currentMeasurement + version: 1 + - id: voltageMeasurement + version: 1 + categories: + - name: CurbPowerMeter +- id: PhaseB + label: Phase B + capabilities: + - id: powerMeter + version: 1 + - id: currentMeasurement + version: 1 + - id: voltageMeasurement + version: 1 + categories: + - name: CurbPowerMeter +- id: PhaseC + label: Phase C + capabilities: + - id: powerMeter + version: 1 + - id: currentMeasurement + version: 1 + - id: voltageMeasurement + version: 1 + categories: + - name: CurbPowerMeter diff --git a/drivers/SmartThings/zigbee-power-meter/src/bituo/can_handle.lua b/drivers/SmartThings/zigbee-power-meter/src/bituo/can_handle.lua new file mode 100644 index 0000000000..a2124afdd6 --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/src/bituo/can_handle.lua @@ -0,0 +1,15 @@ +-- Copyright 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local is_bituo_power_meter = function(opts, driver, device) + local FINGERPRINTS = require("bituo.fingerprints") + for _, fingerprint in ipairs(FINGERPRINTS) do + if device:get_model() == fingerprint.model then + return true, require("bituo") + end + end + + return false +end + +return is_bituo_power_meter diff --git a/drivers/SmartThings/zigbee-power-meter/src/bituo/fingerprints.lua b/drivers/SmartThings/zigbee-power-meter/src/bituo/fingerprints.lua new file mode 100644 index 0000000000..3909881854 --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/src/bituo/fingerprints.lua @@ -0,0 +1,15 @@ +-- Copyright 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + +local ZIGBEE_POWER_METER_FINGERPRINTS = { + { mfr = "BITUO TECHNIK", model = "SPM01-E0" }, + { mfr = "BITUO TECHNIK", model = "SPM01X" }, + { mfr = "BITUO TECHNIK", model = "SDM02-E0" }, + { mfr = "BITUO TECHNIK", model = "SDM02X" }, + { mfr = "BITUO TECHNIK", model = "SPM02-E0" }, + { mfr = "BITUO TECHNIK", model = "SPM02X" }, + { mfr = "BITUO TECHNIK", model = "SDM01W" }, + { mfr = "BITUO TECHNIK", model = "SDM01B" } +} + +return ZIGBEE_POWER_METER_FINGERPRINTS diff --git a/drivers/SmartThings/zigbee-power-meter/src/bituo/init.lua b/drivers/SmartThings/zigbee-power-meter/src/bituo/init.lua new file mode 100644 index 0000000000..4bf5dbd359 --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/src/bituo/init.lua @@ -0,0 +1,231 @@ +-- Copyright 2025 SmartThings, Inc. +-- Licensed under the Apache License, Version 2.0 + + +local capabilities = require "st.capabilities" +local constants = require "st.zigbee.constants" +local clusters = require "st.zigbee.zcl.clusters" +local SimpleMetering = clusters.SimpleMetering +local ElectricalMeasurement = clusters.ElectricalMeasurement +local configurations = require "configurations" + + +local PHASE_A_CONFIGURATION = { + { + cluster = SimpleMetering.ID, + attribute = SimpleMetering.attributes.CurrentSummationDelivered.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = SimpleMetering.attributes.CurrentSummationDelivered.base_type, + reportable_change = 0 + }, + { + cluster = SimpleMetering.ID, + attribute = 0x0001, + minimum_interval = 30, + maximum_interval = 120, + data_type = SimpleMetering.attributes.CurrentSummationDelivered.base_type, + reportable_change = 0 + }, + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.ActivePower.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.ActivePower.base_type, + reportable_change = 0 + }, + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.RMSVoltage.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.RMSVoltage.base_type, + reportable_change = 0 + }, + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.RMSCurrent.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.RMSCurrent.base_type, + reportable_change = 0 + } +} +local PHASE_B_CONFIGURATION = { + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.ActivePowerPhB.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.ActivePowerPhB.base_type, + reportable_change = 0 + }, + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.RMSVoltagePhB.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.RMSVoltagePhB.base_type, + reportable_change = 0 + }, + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.RMSCurrentPhB.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.RMSCurrentPhB.base_type, + reportable_change = 0 + }, +} +local PHASE_C_CONFIGURATION = { + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.ActivePowerPhC.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.ActivePowerPhC.base_type, + reportable_change = 0 + }, + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.RMSVoltagePhC.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.RMSVoltagePhC.base_type, + reportable_change = 0 + }, + { + cluster = ElectricalMeasurement.ID, + attribute = ElectricalMeasurement.attributes.RMSCurrentPhC.ID, + minimum_interval = 30, + maximum_interval = 120, + data_type = ElectricalMeasurement.attributes.RMSCurrentPhC.base_type, + reportable_change = 0 + } +} + +local function energy_handler(driver, device, value, zb_rx) + local multiplier = 1 + local divisor = 100 + local raw_value = value.value + local raw_value_kilowatts = raw_value * multiplier/divisor + + local offset = device:get_field(constants.ENERGY_METER_OFFSET) or 0 + if raw_value_kilowatts < offset then + --- somehow our value has gone below the offset, so we'll reset the offset, since the device seems to have + offset = 0 + device:set_field(constants.ENERGY_METER_OFFSET, offset, {persist = true}) + end + raw_value_kilowatts = raw_value_kilowatts - offset + + local raw_value_watts = raw_value_kilowatts*1000 + local delta_tick + local last_save_ticks = device:get_field("LAST_SAVE_TICK") + + if last_save_ticks == nil then last_save_ticks = 0 end + delta_tick = os.time() - last_save_ticks + + -- wwst energy certification : powerConsumptionReport capability values should be updated every 15 minutes. + -- Check if 15 minutes have passed since the reporting time of current_power_consumption. + if delta_tick >= 15*60 then + local delta_energy = 0.0 + local current_power_consumption = device:get_latest_state("main", capabilities.powerConsumptionReport.ID, capabilities.powerConsumptionReport.powerConsumption.NAME) + if current_power_consumption ~= nil then + delta_energy = math.max(raw_value_watts - current_power_consumption.energy, 0.0) + end + device:emit_event(capabilities.powerConsumptionReport.powerConsumption({energy = raw_value_watts, deltaEnergy = delta_energy })) -- the unit of these values should be 'Wh' + + local curr_save_tick = last_save_ticks + 15*60 -- Set the time to a regular interval by adding 15 minutes to the existing last_save_ticks. + -- If the time 15 minutes from now is less than the current time, set the current time as the last time. + if curr_save_tick + 15*60 < os.time() then + curr_save_tick = os.time() + end + device:set_field("LAST_SAVE_TICK", curr_save_tick, {persist = false}) + end + device:emit_event(capabilities.energyMeter.energy({value = raw_value_kilowatts, unit = "kWh"})) +end + +local function generic_handler_factory(component_name, capability, multiplier, divisor, unit) + return function(driver, device, value, zb_rx) + local component = device.profile.components[component_name] + if component ~= nil then + local raw_value = value.value * multiplier / divisor + device:emit_component_event(component, capability({value = raw_value, unit = unit})) + end + end +end + +local refresh = function(driver, device, cmd) + device:refresh() +end + +local function resetEnergyMeter(self, device) + device:send(clusters.OnOff.server.commands.On(device)) + -- Reset Power consumption + device:set_field(constants.ENERGY_METER_OFFSET, 0, {persist = true}) + device:set_field("LAST_SAVE_TICK", os.time(), {persist = false}) +end +local function do_configure(driver, device) + device:configure() + --device:send(device_management.build_bind_request(device, clusters.SimpleMetering.ID, driver.environment_info.hub_zigbee_eui)) + --device:send(device_management.build_bind_request(device, clusters.ElectricalMeasurement.ID, driver.environment_info.hub_zigbee_eui)) + device:refresh() +end + +local device_init = function(self, device) + for _, attribute in ipairs(PHASE_A_CONFIGURATION) do + device:add_configured_attribute(attribute) + device:add_monitored_attribute(attribute) + end + if string.find(device:get_model(), "SDM02") or string.find(device:get_model(), "SPM02") or string.find(device:get_model(), "SDM01W") then + for _, attribute in ipairs(PHASE_B_CONFIGURATION) do + device:add_configured_attribute(attribute) + device:add_monitored_attribute(attribute) + end + end + if string.find(device:get_model(), "SPM02") or string.find(device:get_model(), "SDM01W") then + for _, attribute in ipairs(PHASE_C_CONFIGURATION) do + device:add_configured_attribute(attribute) + device:add_monitored_attribute(attribute) + end + end +end + +local bituo_power_meter_handler = { + NAME = "bituo power meter handler", + lifecycle_handlers = { + init = configurations.power_reconfig_wrapper(device_init), + doConfigure = do_configure, + }, + zigbee_handlers = { + attr = { + [clusters.SimpleMetering.ID] = { + [clusters.SimpleMetering.attributes.CurrentSummationDelivered.ID] = energy_handler, + [0x0001] = generic_handler_factory("TotalReverseEnergy", capabilities.energyMeter.energy, 1, 100, "kWh"), + }, + [clusters.ElectricalMeasurement.ID] = { + [ElectricalMeasurement.attributes.ActivePower.ID] = generic_handler_factory("PhaseA", capabilities.powerMeter.power, 1, 1, "W"), + [ElectricalMeasurement.attributes.ActivePowerPhB.ID] = generic_handler_factory("PhaseB", capabilities.powerMeter.power, 1, 1, "W"), + [ElectricalMeasurement.attributes.ActivePowerPhC.ID] = generic_handler_factory("PhaseC", capabilities.powerMeter.power, 1, 1, "W"), + [ElectricalMeasurement.attributes.RMSVoltage.ID] = generic_handler_factory("PhaseA", capabilities.voltageMeasurement.voltage, 1, 100, "V"), + [ElectricalMeasurement.attributes.RMSVoltagePhB.ID] = generic_handler_factory("PhaseB", capabilities.voltageMeasurement.voltage, 1, 100, "V"), + [ElectricalMeasurement.attributes.RMSVoltagePhC.ID] = generic_handler_factory("PhaseC", capabilities.voltageMeasurement.voltage, 1, 100, "V"), + [ElectricalMeasurement.attributes.RMSCurrent.ID] = generic_handler_factory("PhaseA", capabilities.currentMeasurement.current, 1, 100, "A"), + [ElectricalMeasurement.attributes.RMSCurrentPhB.ID] = generic_handler_factory("PhaseB", capabilities.currentMeasurement.current, 1, 100, "A"), + [ElectricalMeasurement.attributes.RMSCurrentPhC.ID] = generic_handler_factory("PhaseC", capabilities.currentMeasurement.current, 1, 100, "A") + } + } + }, + capability_handlers = { + [capabilities.refresh.ID] = { + [capabilities.refresh.commands.refresh.NAME] = refresh + }, + [capabilities.energyMeter.ID] = { + [capabilities.energyMeter.commands.resetEnergyMeter.NAME] = resetEnergyMeter, + }, + }, + can_handle = require("bituo.can_handle"), +} + +return bituo_power_meter_handler diff --git a/drivers/SmartThings/zigbee-power-meter/src/sub_drivers.lua b/drivers/SmartThings/zigbee-power-meter/src/sub_drivers.lua index 58ff064c9f..51b24aca32 100644 --- a/drivers/SmartThings/zigbee-power-meter/src/sub_drivers.lua +++ b/drivers/SmartThings/zigbee-power-meter/src/sub_drivers.lua @@ -6,5 +6,6 @@ local sub_drivers = { lazy_load_if_possible("ezex"), lazy_load_if_possible("frient"), lazy_load_if_possible("shinasystems"), + lazy_load_if_possible("bituo"), } return sub_drivers diff --git a/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_1p.lua b/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_1p.lua new file mode 100644 index 0000000000..5308e11ee2 --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_1p.lua @@ -0,0 +1,225 @@ +local test = require "integration_test" +local clusters = require "st.zigbee.zcl.clusters" +local ElectricalMeasurement = clusters.ElectricalMeasurement +local SimpleMetering = clusters.SimpleMetering +local capabilities = require "st.capabilities" +local zigbee_test_utils = require "integration_test.zigbee_test_utils" +local t_utils = require "integration_test.utils" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" + + +-- Mock out globals +local mock_device = test.mock_device.build_test_zigbee_device({ + profile = t_utils.get_profile_definition("power-meter-1p.yml"), + zigbee_endpoints = { + [1] = { + id = 1, + manufacturer = "BITUO TECHNIK", + model = "SPM01X", + server_clusters = {SimpleMetering.ID, ElectricalMeasurement.ID} + } + } +}) + +zigbee_test_utils.prepare_zigbee_env_info() + +local function test_init() + test.mock_device.add_test_device(mock_device) + zigbee_test_utils.init_noop_health_check_timer() +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test( + "SimpleMetering event should be handled by powerConsumptionReport capability", + function() + test.timer.__create_and_queue_test_time_advance_timer(15*60, "oneshot") + -- #1 : 15 minutes have passed + test.mock_time.advance_time(15*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,150) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 1500.0, deltaEnergy = 0.0 })) + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.5, unit = "kWh"})) + ) + -- #2 : Not even 15 minutes passed + test.wait_for_events() + test.mock_time.advance_time(1*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,170) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.7, unit = "kWh"})) + ) + -- #3 : 15 minutes have passed + test.wait_for_events() + test.mock_time.advance_time(14*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,200) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 2000.0, deltaEnergy = 500.0 })) + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 2.0, unit = "kWh"})) + ) + end +) + +test.register_message_test( + "ActivePower Report should be handled. Sensor value is in W, capability attribute value is in hectowatts", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.ActivePower:build_test_attr_report(mock_device, + 27) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.powerMeter.power({ value = 27.0, unit = "W" })) + } + } +) + +test.register_message_test( + "ActivePower Report should be handled. Sensor value is in W, capability attribute value is in hectowatts", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.ActivePower:build_test_attr_report(mock_device, + 27) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.powerMeter.power({ value = 27.0, unit = "W" })) + } + } +) + +test.register_message_test( + "RMSCurrent Report for PhaseA should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSCurrent:build_test_attr_report(mock_device, + 34) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.currentMeasurement.current({ value = 0.34, unit = "A" })) + } + } +) + +test.register_message_test( + "RMSVoltage Report for PhaseA should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSVoltage:build_test_attr_report(mock_device, + 22000) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.voltageMeasurement.voltage({ value = 220.0, unit = "V" })) + } + } +) + +test.register_coroutine_test( + "Device configure lifecycle event should configure device properly", + function() + test.socket.zigbee:__set_channel_ordering("relaxed") + 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, SimpleMetering.ID) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, ElectricalMeasurement.ID) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePower:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltage:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrent:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.configure_reporting(mock_device, data_types.ClusterId(SimpleMetering.ID), data_types.AttributeId(0x0001), data_types.ZigbeeDataType(SimpleMetering.attributes.CurrentSummationDelivered.base_type.ID), 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerDivisor:configure_reporting(mock_device, 1, 43200, 1) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.InstantaneousDemand:configure_reporting(mock_device, 5, 3600, 5) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerMultiplier:configure_reporting(mock_device, 1, 43200, 1) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.InstantaneousDemand:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.read_attribute(mock_device, data_types.ClusterId(SimpleMetering.ID), data_types.AttributeId(0x0001)) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePower:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltage:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrent:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerMultiplier:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerDivisor:read(mock_device) + }) + mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + end +) + +test.run_registered_tests() \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_2p.lua b/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_2p.lua new file mode 100644 index 0000000000..b85c955005 --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_2p.lua @@ -0,0 +1,281 @@ +local test = require "integration_test" +local clusters = require "st.zigbee.zcl.clusters" +local ElectricalMeasurement = clusters.ElectricalMeasurement +local SimpleMetering = clusters.SimpleMetering +local capabilities = require "st.capabilities" +local zigbee_test_utils = require "integration_test.zigbee_test_utils" +local t_utils = require "integration_test.utils" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" + +-- 使用两相电能表配置文件 +local mock_device = test.mock_device.build_test_zigbee_device({ + profile = t_utils.get_profile_definition("power-meter-2p.yml"), + zigbee_endpoints = { + [1] = { + id = 1, + manufacturer = "BITUO TECHNIK", + model = "SDM02X", + server_clusters = {SimpleMetering.ID, ElectricalMeasurement.ID} + } + } +}) + +zigbee_test_utils.prepare_zigbee_env_info() +local function test_init() + test.mock_device.add_test_device(mock_device) + zigbee_test_utils.init_noop_health_check_timer() +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test( + "SimpleMetering event should be handled by powerConsumptionReport capability", + function() + test.timer.__create_and_queue_test_time_advance_timer(15*60, "oneshot") + -- #1 : 15 minutes have passed + test.mock_time.advance_time(15*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,150) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 1500.0, deltaEnergy = 0.0 })) + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.5, unit = "kWh"})) + ) + -- #2 : Not even 15 minutes passed + test.wait_for_events() + test.mock_time.advance_time(1*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,170) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.7, unit = "kWh"})) + ) + -- #3 : 15 minutes have passed + test.wait_for_events() + test.mock_time.advance_time(14*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,200) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 2000.0, deltaEnergy = 500.0 })) + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 2.0, unit = "kWh"})) + ) + end +) + +test.register_message_test( + "ActivePower Report should be handled. Sensor value is in W, capability attribute value is in hectowatts", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.ActivePower:build_test_attr_report(mock_device, + 27) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.powerMeter.power({ value = 27.0, unit = "W" })) + } + } +) + +test.register_message_test( + "RMSCurrent Report for PhaseA should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSCurrent:build_test_attr_report(mock_device, + 34) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.currentMeasurement.current({ value = 0.34, unit = "A" })) + } + } +) + +test.register_message_test( + "RMSVoltage Report for PhaseB should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSVoltage:build_test_attr_report(mock_device, + 22000) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.voltageMeasurement.voltage({ value = 220.0, unit = "V" })) + } + } +) + +test.register_message_test( + "ActivePower Report should be handled. Sensor value is in W, capability attribute value is in hectowatts", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.ActivePowerPhB:build_test_attr_report(mock_device, + 27) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseB", capabilities.powerMeter.power({ value = 27.0, unit = "W" })) + } + } +) + +test.register_message_test( + "RMSCurrent Report for PhaseB should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSCurrentPhB:build_test_attr_report(mock_device, + 34) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseB", capabilities.currentMeasurement.current({ value = 0.34, unit = "A" })) + } + } +) + +test.register_message_test( + "RMSVoltage Report for PhaseB should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSVoltagePhB:build_test_attr_report(mock_device, + 22000) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseB", capabilities.voltageMeasurement.voltage({ value = 220.0, unit = "V" })) + } + } +) + +test.register_coroutine_test( + "Device configure lifecycle event should configure device properly", + function() + test.socket.zigbee:__set_channel_ordering("relaxed") + 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, SimpleMetering.ID) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, ElectricalMeasurement.ID) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePower:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltage:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrent:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePowerPhB:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltagePhB:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrentPhB:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.configure_reporting(mock_device, data_types.ClusterId(SimpleMetering.ID), data_types.AttributeId(0x0001), data_types.ZigbeeDataType(SimpleMetering.attributes.CurrentSummationDelivered.base_type.ID), 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.read_attribute(mock_device, data_types.ClusterId(SimpleMetering.ID), data_types.AttributeId(0x0001)) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePower:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltage:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrent:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerMultiplier:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerDivisor:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePowerPhB:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltagePhB:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrentPhB:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.InstantaneousDemand:configure_reporting(mock_device, 5, 3600, 5) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerDivisor:configure_reporting(mock_device, 1, 43200, 1) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerMultiplier:configure_reporting(mock_device, 1, 43200, 1) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.InstantaneousDemand:read(mock_device) + }) + mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + end +) + +test.run_registered_tests() \ No newline at end of file diff --git a/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_3p.lua b/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_3p.lua new file mode 100644 index 0000000000..fad26aad40 --- /dev/null +++ b/drivers/SmartThings/zigbee-power-meter/src/test/test_zigbee_power_meter_3p.lua @@ -0,0 +1,355 @@ +local test = require "integration_test" +local clusters = require "st.zigbee.zcl.clusters" +local ElectricalMeasurement = clusters.ElectricalMeasurement +local SimpleMetering = clusters.SimpleMetering +local capabilities = require "st.capabilities" +local zigbee_test_utils = require "integration_test.zigbee_test_utils" +local t_utils = require "integration_test.utils" +local cluster_base = require "st.zigbee.cluster_base" +local data_types = require "st.zigbee.data_types" + +local mock_device = test.mock_device.build_test_zigbee_device({ + profile = t_utils.get_profile_definition("power-meter-3p.yml"), + zigbee_endpoints = { + [1] = { + id = 1, + manufacturer = "BITUO TECHNIK", + model = "SDM01W", + server_clusters = {SimpleMetering.ID, ElectricalMeasurement.ID} + } + } +}) + +zigbee_test_utils.prepare_zigbee_env_info() +local function test_init() + test.mock_device.add_test_device(mock_device) + zigbee_test_utils.init_noop_health_check_timer() +end + +test.set_test_init_function(test_init) + +test.register_coroutine_test( + "SimpleMetering event should be handled by powerConsumptionReport capability", + function() + test.timer.__create_and_queue_test_time_advance_timer(15*60, "oneshot") + -- #1 : 15 minutes have passed + test.mock_time.advance_time(15*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,150) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 1500.0, deltaEnergy = 0.0 })) + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.5, unit = "kWh"})) + ) + -- #2 : Not even 15 minutes passed + test.wait_for_events() + test.mock_time.advance_time(1*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,170) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.7, unit = "kWh"})) + ) + -- #3 : 15 minutes have passed + test.wait_for_events() + test.mock_time.advance_time(14*60) + test.socket.zigbee:__queue_receive({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,200) + }) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 2000.0, deltaEnergy = 500.0 })) + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 2.0, unit = "kWh"})) + ) + end +) + +test.register_message_test( + "ActivePower Report should be handled. Sensor value is in W, capability attribute value is in hectowatts", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.ActivePower:build_test_attr_report(mock_device, + 27) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.powerMeter.power({ value = 27.0, unit = "W" })) + } + } +) + +test.register_message_test( + "RMSCurrent Report for PhaseA should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSCurrent:build_test_attr_report(mock_device, + 34) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.currentMeasurement.current({ value = 0.34, unit = "A" })) + } + } +) + +test.register_message_test( + "RMSVoltage Report for PhaseA should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSVoltage:build_test_attr_report(mock_device, + 22000) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseA", capabilities.voltageMeasurement.voltage({ value = 220.0, unit = "V" })) + } + } +) + +test.register_message_test( + "ActivePower Report for PhaseB should be handled. Sensor value is in W, capability attribute value is in hectowatts", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.ActivePowerPhB:build_test_attr_report(mock_device, + 27) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseB", capabilities.powerMeter.power({ value = 27.0, unit = "W" })) + } + } +) + +test.register_message_test( + "RMSCurrent Report for PhaseB should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSCurrentPhB:build_test_attr_report(mock_device, + 34) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseB", capabilities.currentMeasurement.current({ value = 0.34, unit = "A" })) + } + } +) + +test.register_message_test( + "RMSVoltage Report for PhaseB should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSVoltagePhB:build_test_attr_report(mock_device, + 22000) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseB", capabilities.voltageMeasurement.voltage({ value = 220.0, unit = "V" })) + } + } +) + +test.register_message_test( + "ActivePower Report for PhaseC should be handled. Sensor value is in W, capability attribute value is in hectowatts", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.ActivePowerPhC:build_test_attr_report(mock_device, + 27) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseC", capabilities.powerMeter.power({ value = 27.0, unit = "W" })) + } + } +) + +test.register_message_test( + "RMSCurrent Report for PhaseC should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSCurrentPhC:build_test_attr_report(mock_device, + 34) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseC", capabilities.currentMeasurement.current({ value = 0.34, unit = "A" })) + } + } +) + +test.register_message_test( + "RMSVoltage Report for PhaseC should be handled", + { + { + channel = "zigbee", + direction = "receive", + message = { mock_device.id, ElectricalMeasurement.attributes.RMSVoltagePhC:build_test_attr_report(mock_device, + 22000) }, + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("PhaseC", capabilities.voltageMeasurement.voltage({ value = 220.0, unit = "V" })) + } + } +) + +test.register_coroutine_test( + "Device configure lifecycle event should configure device properly", + function() + test.socket.zigbee:__set_channel_ordering("relaxed") + 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, SimpleMetering.ID) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, ElectricalMeasurement.ID) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePower:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltage:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrent:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePowerPhB:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltagePhB:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrentPhB:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePowerPhC:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltagePhC:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrentPhC:configure_reporting(mock_device, 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.configure_reporting(mock_device, data_types.ClusterId(SimpleMetering.ID), data_types.AttributeId(0x0001), data_types.ZigbeeDataType(SimpleMetering.attributes.CurrentSummationDelivered.base_type.ID), 30, 120, 0) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + cluster_base.read_attribute(mock_device, data_types.ClusterId(SimpleMetering.ID), data_types.AttributeId(0x0001)) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.CurrentSummationDelivered:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePower:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltage:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrent:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerMultiplier:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerDivisor:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePowerPhB:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltagePhB:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrentPhB:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ActivePowerPhC:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSVoltagePhC:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.RMSCurrentPhC:read(mock_device) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.InstantaneousDemand:configure_reporting(mock_device, 5, 3600, 5) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerDivisor:configure_reporting(mock_device, 1, 43200, 1) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + ElectricalMeasurement.attributes.ACPowerMultiplier:configure_reporting(mock_device, 1, 43200, 1) + }) + test.socket.zigbee:__expect_send({ + mock_device.id, + SimpleMetering.attributes.InstantaneousDemand:read(mock_device) + }) + mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + end +) + +test.run_registered_tests() \ No newline at end of file