Skip to content

Commit d907021

Browse files
committed
add MultiIR Smoke Detector MIR-SM200
1 parent f8db0da commit d907021

File tree

8 files changed

+319
-0
lines changed

8 files changed

+319
-0
lines changed

drivers/SmartThings/zigbee-smoke-detector/fingerprints.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@ zigbeeManufacturer:
5454
manufacturer: HEIMAN
5555
model: GASSensor-N
5656
deviceProfileName: smoke-detector
57+
- id: "MultIR/MIR-SM200"
58+
deviceLabel: MultiIR Smoke Detector MIR-SM200
59+
manufacturer: MultIR
60+
model: MIR-SM200
61+
deviceProfileName: smoke-battery-tamper
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: smoke-battery-tamper
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: smokeDetector
6+
version: 1
7+
- id: tamperAlert
8+
version: 1
9+
- id: battery
10+
version: 1
11+
- id: firmwareUpdate
12+
version: 1
13+
- id: refresh
14+
version: 1
15+
categories:
16+
- name: SmokeDetector
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- Copyright 2026 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
3+
4+
return function(opts, driver, device, ...)
5+
local FINGERPRINTS = require "MultiIR.fingerprints"
6+
for _, fingerprint in ipairs(FINGERPRINTS) do
7+
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
8+
local subdriver = require("MultiIR")
9+
return true, subdriver
10+
end
11+
end
12+
return false
13+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Copyright 2026 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
3+
4+
return {
5+
{ mfr = "MultIR", model = "MIR-SM200" }
6+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
-- Copyright 2026 SmartThings, Inc.
2+
-- Licensed under the Apache License, Version 2.0
3+
4+
local zcl_clusters = require "st.zigbee.zcl.clusters"
5+
local capabilities = require "st.capabilities"
6+
7+
local IASZone = zcl_clusters.IASZone
8+
9+
local function generate_event_from_zone_status(driver, device, zone_status, zb_rx)
10+
if zone_status:is_alarm1_set() then
11+
device:emit_event(capabilities.smokeDetector.smoke.detected())
12+
elseif zone_status:is_alarm2_set() then
13+
device:emit_event(capabilities.smokeDetector.smoke.tested())
14+
else
15+
device:emit_event(capabilities.smokeDetector.smoke.clear())
16+
end
17+
if device:supports_capability(capabilities.tamperAlert) then
18+
device:emit_event(zone_status:is_tamper_set() and capabilities.tamperAlert.tamper.detected() or capabilities.tamperAlert.tamper.clear())
19+
end
20+
end
21+
22+
local function ias_zone_status_attr_handler(driver, device, zone_status, zb_rx)
23+
generate_event_from_zone_status(driver, device, zone_status, zb_rx)
24+
end
25+
26+
local function ias_zone_status_change_handler(driver, device, zb_rx)
27+
local zone_status = zb_rx.body.zcl_body.zone_status
28+
generate_event_from_zone_status(driver, device, zone_status, zb_rx)
29+
end
30+
31+
local function added_handler(self, device)
32+
device:emit_event(capabilities.battery.battery(100))
33+
device:emit_event(capabilities.smokeDetector.smoke.clear())
34+
device:emit_event(capabilities.tamperAlert.tamper.clear())
35+
end
36+
37+
local function do_configure(self, device)
38+
device:configure()
39+
end
40+
41+
local MultiIR_smoke_detector_handler = {
42+
NAME = "MultiIR Smoke Detector Handler",
43+
lifecycle_handlers = {
44+
added = added_handler,
45+
doConfigure = do_configure
46+
},
47+
zigbee_handlers = {
48+
cluster = {
49+
[IASZone.ID] = {
50+
[IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler
51+
}
52+
},
53+
attr = {
54+
[IASZone.ID] = {
55+
[IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler
56+
}
57+
}
58+
},
59+
can_handle = require("MultiIR.can_handle")
60+
}
61+
62+
return MultiIR_smoke_detector_handler

drivers/SmartThings/zigbee-smoke-detector/src/sub_drivers.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ local sub_drivers = {
66
lazy_load_if_possible("frient"),
77
lazy_load_if_possible("aqara-gas"),
88
lazy_load_if_possible("aqara"),
9+
lazy_load_if_possible("MultiIR"),
910
}
1011
return sub_drivers
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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 capabilities = require "st.capabilities"
7+
local t_utils = require "integration_test.utils"
8+
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
9+
10+
local IASZone = clusters.IASZone
11+
local PowerConfiguration = clusters.PowerConfiguration
12+
13+
local mock_device = test.mock_device.build_test_zigbee_device(
14+
{ profile = t_utils.get_profile_definition("smoke-battery-tamper.yml"),
15+
zigbee_endpoints = {
16+
[0x01] = {
17+
id = 0x01,
18+
manufacturer = "MultIR",
19+
model = "MIR-SM200",
20+
server_clusters = { 0x0001,0x0003, 0x0005, 0x0006 }
21+
}
22+
}
23+
}
24+
)
25+
26+
zigbee_test_utils.prepare_zigbee_env_info()
27+
28+
local function test_init()
29+
test.mock_device.add_test_device(mock_device)
30+
end
31+
32+
test.set_test_init_function(test_init)
33+
34+
test.register_coroutine_test(
35+
"Handle added lifecycle",
36+
function()
37+
test.socket.zigbee:__set_channel_ordering("relaxed")
38+
test.socket.capability:__set_channel_ordering("relaxed")
39+
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "added" })
40+
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
41+
capabilities.battery.battery(100)))
42+
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
43+
capabilities.smokeDetector.smoke.clear()))
44+
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
45+
capabilities.tamperAlert.tamper.clear()))
46+
end,
47+
{
48+
min_api_version = 19
49+
}
50+
)
51+
52+
test.register_coroutine_test(
53+
"Handle doConfigure lifecycle",
54+
function()
55+
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
56+
test.socket.zigbee:__expect_send({
57+
mock_device.id,
58+
zigbee_test_utils.build_bind_request(mock_device, zigbee_test_utils.mock_hub_eui, PowerConfiguration.ID)
59+
})
60+
test.socket.zigbee:__expect_send({
61+
mock_device.id,
62+
PowerConfiguration.attributes.BatteryPercentageRemaining:configure_reporting(mock_device, 0x001e, 0x5460, 1)
63+
})
64+
mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
65+
end,
66+
{
67+
min_api_version = 19
68+
}
69+
)
70+
71+
test.register_message_test(
72+
"Reported ZoneStatus should be handled: smoke/clear tamper/clear",
73+
{
74+
{
75+
channel = "zigbee",
76+
direction = "receive",
77+
message = { mock_device.id, IASZone.attributes.ZoneStatus:build_test_attr_report(mock_device, 0x0000) }
78+
},
79+
{
80+
channel = "capability",
81+
direction = "send",
82+
message = mock_device:generate_test_message("main", capabilities.smokeDetector.smoke.clear())
83+
},
84+
{
85+
channel = "capability",
86+
direction = "send",
87+
message = mock_device:generate_test_message("main", capabilities.tamperAlert.tamper.clear())
88+
}
89+
},
90+
{
91+
min_api_version = 19
92+
}
93+
)
94+
95+
test.register_message_test(
96+
"Reported ZoneStatus should be handled: smoke/detected tamper/detected",
97+
{
98+
{
99+
channel = "zigbee",
100+
direction = "receive",
101+
message = { mock_device.id, IASZone.attributes.ZoneStatus:build_test_attr_report(mock_device, 0x0005) }
102+
},
103+
{
104+
channel = "capability",
105+
direction = "send",
106+
message = mock_device:generate_test_message("main", capabilities.smokeDetector.smoke.detected())
107+
},
108+
{
109+
channel = "capability",
110+
direction = "send",
111+
message = mock_device:generate_test_message("main", capabilities.tamperAlert.tamper.detected())
112+
}
113+
},
114+
{
115+
min_api_version = 19
116+
}
117+
)
118+
119+
test.register_message_test(
120+
"Reported ZoneStatus should be handled: smoke/tested tamper/detected",
121+
{
122+
{
123+
channel = "zigbee",
124+
direction = "receive",
125+
message = { mock_device.id, IASZone.attributes.ZoneStatus:build_test_attr_report(mock_device, 0x0006) }
126+
},
127+
{
128+
channel = "capability",
129+
direction = "send",
130+
message = mock_device:generate_test_message("main", capabilities.smokeDetector.smoke.tested())
131+
},
132+
{
133+
channel = "capability",
134+
direction = "send",
135+
message = mock_device:generate_test_message("main", capabilities.tamperAlert.tamper.detected())
136+
}
137+
},
138+
{
139+
min_api_version = 19
140+
}
141+
)
142+
143+
test.register_message_test(
144+
"ZoneStatusChangeNotification should be handled: smoke/detected tamper/detected",
145+
{
146+
{
147+
channel = "zigbee",
148+
direction = "receive",
149+
message = { mock_device.id, IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(mock_device, 0x0005, 0x00) }
150+
},
151+
{
152+
channel = "capability",
153+
direction = "send",
154+
message = mock_device:generate_test_message("main", capabilities.smokeDetector.smoke.detected())
155+
},
156+
{
157+
channel = "capability",
158+
direction = "send",
159+
message = mock_device:generate_test_message("main", capabilities.tamperAlert.tamper.detected())
160+
}
161+
},
162+
{
163+
min_api_version = 19
164+
}
165+
)
166+
167+
test.register_message_test(
168+
"ZoneStatusChangeNotification should be handled: smoke/tested tamper/detected",
169+
{
170+
{
171+
channel = "zigbee",
172+
direction = "receive",
173+
message = { mock_device.id, IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(mock_device, 0x0006, 0x00) }
174+
},
175+
{
176+
channel = "capability",
177+
direction = "send",
178+
message = mock_device:generate_test_message("main", capabilities.smokeDetector.smoke.tested())
179+
},
180+
{
181+
channel = "capability",
182+
direction = "send",
183+
message = mock_device:generate_test_message("main", capabilities.tamperAlert.tamper.detected())
184+
}
185+
},
186+
{
187+
min_api_version = 19
188+
}
189+
)
190+
191+
test.register_message_test(
192+
"ZoneStatusChangeNotification should be handled: smoke/clear tamper/clear",
193+
{
194+
{
195+
channel = "zigbee",
196+
direction = "receive",
197+
message = { mock_device.id, IASZone.client.commands.ZoneStatusChangeNotification.build_test_rx(mock_device, 0x0000, 0x00) }
198+
},
199+
{
200+
channel = "capability",
201+
direction = "send",
202+
message = mock_device:generate_test_message("main", capabilities.smokeDetector.smoke.clear())
203+
},
204+
{
205+
channel = "capability",
206+
direction = "send",
207+
message = mock_device:generate_test_message("main", capabilities.tamperAlert.tamper.clear())
208+
}
209+
},
210+
{
211+
min_api_version = 19
212+
}
213+
)
214+
215+
test.run_registered_tests()

tools/localizations/cn.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,4 @@ Aqara Wireless Mini Switch T1,Aqara 无线开关 T1
134134
"WISTAR WSCMXJ Smart Curtain Motor",威仕达智能开合帘电机 WSCMXJ
135135
"HAOJAI Smart Switch 3-key",好家智能三键开关
136136
"HAOJAI Smart Switch 6-key",好家智能六键开关
137+
"MultiIR Smoke Detector MIR-SM200",麦乐克烟雾报警器MIR-SM200

0 commit comments

Comments
 (0)