Skip to content

Commit 8d9f3a7

Browse files
authored
Add BITUO TECHNIK OEM devices for the Zemismart manufacturer (#2986)
* Add BituoTechnik's OEM device --Zemismart * Add Zemismart device test file * Adjust the format
1 parent 5027388 commit 8d9f3a7

6 files changed

Lines changed: 886 additions & 3 deletions

File tree

drivers/SmartThings/zigbee-power-meter/fingerprints.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,31 @@ zigbeeManufacturer:
8383
manufacturer: BITUO TECHNIK
8484
model: "SDM01B"
8585
deviceProfileName: power-meter-1p
86+
- id: "Zemismart/SPM01-1Z2"
87+
deviceLabel: Energy Monitor 1PN
88+
manufacturer: Zemismart
89+
model: "SPM01-1Z2"
90+
deviceProfileName: power-meter-1p
91+
- id: "Zemismart/SDM02-2Z1"
92+
deviceLabel: Energy Monitor 2PN
93+
manufacturer: Zemismart
94+
model: "SDM02-2Z1"
95+
deviceProfileName: power-meter-2p
96+
- id: "Zemismart/SPM02-3Z3"
97+
deviceLabel: Energy Monitor 3PN
98+
manufacturer: Zemismart
99+
model: "SPM02-3Z3"
100+
deviceProfileName: power-meter-3p
101+
- id: "Zemismart/SDM01-3Z1"
102+
deviceLabel: Energy Monitor 3PN
103+
manufacturer: Zemismart
104+
model: "SDM01-3Z1"
105+
deviceProfileName: power-meter-3p
106+
- id: "Zemismart/SDM01-1Z1"
107+
deviceLabel: Energy Monitor 1PN
108+
manufacturer: Zemismart
109+
model: "SDM01-1Z1"
110+
deviceProfileName: power-meter-1p
86111
zigbeeGeneric:
87112
- id: "genericMeter"
88113
deviceLabel: Zigbee Meter

drivers/SmartThings/zigbee-power-meter/src/bituo/fingerprints.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ local ZIGBEE_POWER_METER_FINGERPRINTS = {
99
{ mfr = "BITUO TECHNIK", model = "SPM02-E0" },
1010
{ mfr = "BITUO TECHNIK", model = "SPM02X" },
1111
{ mfr = "BITUO TECHNIK", model = "SDM01W" },
12-
{ mfr = "BITUO TECHNIK", model = "SDM01B" }
12+
{ mfr = "BITUO TECHNIK", model = "SDM01B" },
13+
{ mfr = "Zemismart", model = "SPM01-1Z2" },
14+
{ mfr = "Zemismart", model = "SPM02-3Z3" },
15+
{ mfr = "Zemismart", model = "SDM01-3Z1" },
16+
{ mfr = "Zemismart", model = "SDM01-1Z1" },
17+
{ mfr = "Zemismart", model = "SDM02-2Z1" }
1318
}
1419

1520
return ZIGBEE_POWER_METER_FINGERPRINTS

drivers/SmartThings/zigbee-power-meter/src/bituo/init.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,13 @@ local device_init = function(self, device)
178178
device:add_configured_attribute(attribute)
179179
device:add_monitored_attribute(attribute)
180180
end
181-
if string.find(device:get_model(), "SDM02") or string.find(device:get_model(), "SPM02") or string.find(device:get_model(), "SDM01W") then
181+
if string.find(device:get_model(), "SDM02") or string.find(device:get_model(), "SPM02") or string.find(device:get_model(), "SDM01W") or string.find(device:get_model(), "SDM01-3Z1", 1, true) then
182182
for _, attribute in ipairs(PHASE_B_CONFIGURATION) do
183183
device:add_configured_attribute(attribute)
184184
device:add_monitored_attribute(attribute)
185185
end
186186
end
187-
if string.find(device:get_model(), "SPM02") or string.find(device:get_model(), "SDM01W") then
187+
if string.find(device:get_model(), "SPM02") or string.find(device:get_model(), "SDM01W") or string.find(device:get_model(), "SDM01-3Z1", 1, true) then
188188
for _, attribute in ipairs(PHASE_C_CONFIGURATION) do
189189
device:add_configured_attribute(attribute)
190190
device:add_monitored_attribute(attribute)
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
-- Copyright 2026 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
3+
4+
local test = require "integration_test"
5+
local clusters = require "st.zigbee.zcl.clusters"
6+
local ElectricalMeasurement = clusters.ElectricalMeasurement
7+
local SimpleMetering = clusters.SimpleMetering
8+
local capabilities = require "st.capabilities"
9+
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
10+
local t_utils = require "integration_test.utils"
11+
local cluster_base = require "st.zigbee.cluster_base"
12+
local data_types = require "st.zigbee.data_types"
13+
14+
15+
-- Mock out globals
16+
local mock_device = test.mock_device.build_test_zigbee_device({
17+
profile = t_utils.get_profile_definition("power-meter-1p.yml"),
18+
zigbee_endpoints = {
19+
[1] = {
20+
id = 1,
21+
manufacturer = "Zemismart",
22+
model = "SPM01-1Z2",
23+
server_clusters = {SimpleMetering.ID, ElectricalMeasurement.ID}
24+
}
25+
}
26+
})
27+
28+
zigbee_test_utils.prepare_zigbee_env_info()
29+
30+
local function test_init()
31+
test.mock_device.add_test_device(mock_device)
32+
zigbee_test_utils.init_noop_health_check_timer()
33+
end
34+
35+
test.set_test_init_function(test_init)
36+
37+
test.register_coroutine_test(
38+
"SimpleMetering event should be handled by powerConsumptionReport capability",
39+
function()
40+
test.timer.__create_and_queue_test_time_advance_timer(15*60, "oneshot")
41+
-- #1 : 15 minutes have passed
42+
test.mock_time.advance_time(15*60)
43+
test.socket.zigbee:__queue_receive({
44+
mock_device.id,
45+
SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,150)
46+
})
47+
test.socket.capability:__expect_send(
48+
mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 1500.0, deltaEnergy = 0.0 }))
49+
)
50+
test.socket.capability:__expect_send(
51+
mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.5, unit = "kWh"}))
52+
)
53+
-- #2 : Not even 15 minutes passed
54+
test.wait_for_events()
55+
test.mock_time.advance_time(1*60)
56+
test.socket.zigbee:__queue_receive({
57+
mock_device.id,
58+
SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,170)
59+
})
60+
test.socket.capability:__expect_send(
61+
mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 1.7, unit = "kWh"}))
62+
)
63+
-- #3 : 15 minutes have passed
64+
test.wait_for_events()
65+
test.mock_time.advance_time(14*60)
66+
test.socket.zigbee:__queue_receive({
67+
mock_device.id,
68+
SimpleMetering.attributes.CurrentSummationDelivered:build_test_attr_report(mock_device,200)
69+
})
70+
test.socket.capability:__expect_send(
71+
mock_device:generate_test_message("main", capabilities.powerConsumptionReport.powerConsumption({energy = 2000.0, deltaEnergy = 500.0 }))
72+
)
73+
test.socket.capability:__expect_send(
74+
mock_device:generate_test_message("main", capabilities.energyMeter.energy({value = 2.0, unit = "kWh"}))
75+
)
76+
end
77+
)
78+
79+
test.register_message_test(
80+
"ActivePower Report should be handled. Sensor value is in W, capability attribute value is in hectowatts",
81+
{
82+
{
83+
channel = "zigbee",
84+
direction = "receive",
85+
message = { mock_device.id, ElectricalMeasurement.attributes.ActivePower:build_test_attr_report(mock_device,
86+
27) },
87+
},
88+
{
89+
channel = "capability",
90+
direction = "send",
91+
message = mock_device:generate_test_message("PhaseA", capabilities.powerMeter.power({ value = 27.0, unit = "W" }))
92+
}
93+
}
94+
)
95+
96+
test.register_message_test(
97+
"RMSCurrent Report for PhaseA should be handled",
98+
{
99+
{
100+
channel = "zigbee",
101+
direction = "receive",
102+
message = { mock_device.id, ElectricalMeasurement.attributes.RMSCurrent:build_test_attr_report(mock_device,
103+
34) },
104+
},
105+
{
106+
channel = "capability",
107+
direction = "send",
108+
message = mock_device:generate_test_message("PhaseA", capabilities.currentMeasurement.current({ value = 0.34, unit = "A" }))
109+
}
110+
}
111+
)
112+
113+
test.register_message_test(
114+
"RMSVoltage Report for PhaseA should be handled",
115+
{
116+
{
117+
channel = "zigbee",
118+
direction = "receive",
119+
message = { mock_device.id, ElectricalMeasurement.attributes.RMSVoltage:build_test_attr_report(mock_device,
120+
22000) },
121+
},
122+
{
123+
channel = "capability",
124+
direction = "send",
125+
message = mock_device:generate_test_message("PhaseA", capabilities.voltageMeasurement.voltage({ value = 220.0, unit = "V" }))
126+
}
127+
}
128+
)
129+
130+
test.register_coroutine_test(
131+
"Device configure lifecycle event should configure device properly",
132+
function()
133+
test.socket.zigbee:__set_channel_ordering("relaxed")
134+
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
135+
test.socket.zigbee:__expect_send({
136+
mock_device.id,
137+
zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, SimpleMetering.ID)
138+
})
139+
test.socket.zigbee:__expect_send({
140+
mock_device.id,
141+
zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, ElectricalMeasurement.ID)
142+
})
143+
test.socket.zigbee:__expect_send({
144+
mock_device.id,
145+
SimpleMetering.attributes.CurrentSummationDelivered:configure_reporting(mock_device, 30, 120, 0)
146+
})
147+
test.socket.zigbee:__expect_send({
148+
mock_device.id,
149+
ElectricalMeasurement.attributes.ActivePower:configure_reporting(mock_device, 30, 120, 0)
150+
})
151+
test.socket.zigbee:__expect_send({
152+
mock_device.id,
153+
ElectricalMeasurement.attributes.RMSVoltage:configure_reporting(mock_device, 30, 120, 0)
154+
})
155+
test.socket.zigbee:__expect_send({
156+
mock_device.id,
157+
ElectricalMeasurement.attributes.RMSCurrent:configure_reporting(mock_device, 30, 120, 0)
158+
})
159+
test.socket.zigbee:__expect_send({
160+
mock_device.id,
161+
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)
162+
})
163+
test.socket.zigbee:__expect_send({
164+
mock_device.id,
165+
ElectricalMeasurement.attributes.ACPowerDivisor:configure_reporting(mock_device, 1, 43200, 1)
166+
})
167+
test.socket.zigbee:__expect_send({
168+
mock_device.id,
169+
SimpleMetering.attributes.InstantaneousDemand:configure_reporting(mock_device, 5, 3600, 5)
170+
})
171+
test.socket.zigbee:__expect_send({
172+
mock_device.id,
173+
ElectricalMeasurement.attributes.ACPowerMultiplier:configure_reporting(mock_device, 1, 43200, 1)
174+
})
175+
test.socket.zigbee:__expect_send({
176+
mock_device.id,
177+
SimpleMetering.attributes.InstantaneousDemand:read(mock_device)
178+
})
179+
test.socket.zigbee:__expect_send({
180+
mock_device.id,
181+
cluster_base.read_attribute(mock_device, data_types.ClusterId(SimpleMetering.ID), data_types.AttributeId(0x0001))
182+
})
183+
test.socket.zigbee:__expect_send({
184+
mock_device.id,
185+
SimpleMetering.attributes.CurrentSummationDelivered:read(mock_device)
186+
})
187+
test.socket.zigbee:__expect_send({
188+
mock_device.id,
189+
ElectricalMeasurement.attributes.ActivePower:read(mock_device)
190+
})
191+
test.socket.zigbee:__expect_send({
192+
mock_device.id,
193+
ElectricalMeasurement.attributes.RMSVoltage:read(mock_device)
194+
})
195+
test.socket.zigbee:__expect_send({
196+
mock_device.id,
197+
ElectricalMeasurement.attributes.RMSCurrent:read(mock_device)
198+
})
199+
test.socket.zigbee:__expect_send({
200+
mock_device.id,
201+
ElectricalMeasurement.attributes.ACPowerMultiplier:read(mock_device)
202+
})
203+
test.socket.zigbee:__expect_send({
204+
mock_device.id,
205+
ElectricalMeasurement.attributes.ACPowerDivisor:read(mock_device)
206+
})
207+
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
208+
end
209+
)
210+
211+
test.run_registered_tests()

0 commit comments

Comments
 (0)