Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions drivers/SmartThings/matter-lock/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ matterManufacturer:
vendorId: 0x115F
productId: 0x286A
deviceProfileName: lock-user-pin
- id: "4447/10244"
deviceLabel: Aqara Smart Lock U400
vendorId: 0x115F
productId: 0x2804
deviceProfileName: lock
#Eufy
- id: "5427/1"
deviceLabel: eufy Smart Lock E31
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ local NEW_MATTER_LOCK_PRODUCTS = {
{0x1421, 0x0042}, -- Kwikset Halo Select Plus
{0x1421, 0x0081}, -- Kwikset Aura Reach
{0x1236, 0xa538}, -- Schlage Sense Pro
{0x1236, 0x3800}, -- Schlage
{0x1236, 0xA738} -- Schlage
}

return NEW_MATTER_LOCK_PRODUCTS
110 changes: 109 additions & 1 deletion drivers/SmartThings/matter-lock/src/test/test_new_matter_lock.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2100,7 +2100,6 @@ test.register_coroutine_test(
}
)

-- mock_device:set_field(lock_utils.COTA_CRED, "654123", {persist = true}) --overwrite random cred for test expectation
test.register_coroutine_test(
"Add Guest User and failure response ",
function()
Expand Down Expand Up @@ -2232,4 +2231,113 @@ test.register_coroutine_test(
}
)

test.register_coroutine_test(
"Add Guest User and failure response, and ClearUser command fails as well",
function()
test.socket.capability:__queue_receive(
{
mock_device.id,
{
capability = capabilities.lockCredentials.ID,
command = "addCredential",
args = {0, "guest", "pin", "654123"}
},
}
)
test.socket.matter:__expect_send(
{
mock_device.id,
DoorLock.server.commands.SetCredential(
mock_device, 1, -- endpoint
DoorLock.types.DataOperationTypeEnum.ADD, -- operation_type
DoorLock.types.CredentialStruct(
{credential_type = DoorLock.types.CredentialTypeEnum.PIN, credential_index = 1}
), -- credential
"654123", -- credential_data
nil, -- user_index
nil, -- user_status
DoorLock.types.UserTypeEnum.SCHEDULE_RESTRICTED_USER -- user_type
),
}
)
test.wait_for_events()
test.socket.matter:__queue_receive(
{
mock_device.id,
DoorLock.client.commands.SetCredentialResponse:build_test_command_response(
mock_device, 1,
DoorLock.types.DlStatus.SUCCESS, -- status
1, -- user_index
2 -- next_credential_index
),
}
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main",
capabilities.lockUsers.users({{userIndex = 1, userType = "guest"}}, {visibility={displayed=false}})
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main",
capabilities.lockCredentials.credentials(
{{credentialIndex=1, credentialType="pin", userIndex=1}}, {visibility={displayed=false}}
)
)
)
test.socket.matter:__expect_send(
{
mock_device.id,
DoorLock.server.commands.SetYearDaySchedule(
mock_device, 1, -- endpoint
1, -- year_day_index
1, -- user_index
0, -- local_start_time
0xffffffff -- local_end_time
),
}
)
test.wait_for_events()
test.socket.matter:__queue_receive(
{
mock_device.id,
DoorLock.server.commands.SetYearDaySchedule:build_test_command_response(
mock_device, 1,
DoorLock.types.DlStatus.FAILURE -- status
),
}
)
test.socket.matter:__expect_send({
mock_device.id,
DoorLock.server.commands.ClearUser(
mock_device, 1,
1
)
})
test.wait_for_events()
test.socket.matter:__queue_receive(
{
mock_device.id,
DoorLock.server.commands.ClearUser:build_test_command_response(
mock_device, 1,
DoorLock.types.DlStatus.FAILURE -- status
),
}
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main",
capabilities.lockCredentials.commandResult(
{commandName="addCredential", statusCode="success", userIndex=1}, {state_change=true, visibility={displayed=false}}
)
)
)
end,
{
min_api_version = 17
}
)


test.run_registered_tests()
37 changes: 36 additions & 1 deletion drivers/SmartThings/matter-switch/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,26 @@ matterManufacturer:
vendorId: 0x1396
productId: 0x10B1
deviceProfileName: light-level-colorTemperature
- id: "5014/4164"
deviceLabel: Linkind Smart Light Bulb
vendorId: 0x1396
productId: 0x1044
deviceProfileName: light-color-level
- id: "5014/4118"
deviceLabel: Linkind Smart Filament Bulb
vendorId: 0x1396
productId: 0x1016
deviceProfileName: light-color-level
- id: "5014/4116"
deviceLabel: Linkind Smart Light Bulb
vendorId: 0x1396
productId: 0x1014
deviceProfileName: light-color-level
- id: "5014/4165"
deviceLabel: Linkind Smart Downlight
vendorId: 0x1396
productId: 0x1045
deviceProfileName: light-color-level
#Bosch Smart Home
- id: "4617/12310"
deviceLabel: Plug Compact [M]
Expand Down Expand Up @@ -674,7 +694,7 @@ matterManufacturer:
productId: 0x61B9
deviceProfileName: light-color-level
- id: "4999/24742"
deviceLabel: Govee Ceiling Light Pro (15 inch)
deviceLabel: Govee Ceiling Light Pro
vendorId: 0x1387
productId: 0x60A6
deviceProfileName: light-color-level
Expand Down Expand Up @@ -813,6 +833,21 @@ matterManufacturer:
vendorId: 0x1387
productId: 0x1741
deviceProfileName: light-color-level
- id: "4999/5808"
deviceLabel: Govee Floor Lamp 3
vendorId: 0x1387
productId: 0x16B0
deviceProfileName: light-color-level
- id: "4999/5824"
deviceLabel: Govee Floor Lamp 3 Lite
vendorId: 0x1387
productId: 0x16C0
deviceProfileName: light-color-level
- id: "4999/4720"
deviceLabel: Govee Ceiling Light Ultra (21inch)
vendorId: 0x1387
productId: 0x1270
deviceProfileName: light-color-level
# Hager
- id: "4741/8"
deviceLabel: Hager matter 2 buttons (battery)
Expand Down
5 changes: 5 additions & 0 deletions drivers/SmartThings/zigbee-siren/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ zigbeeManufacturer :
manufacturer: Sercomm Corp.
model: SZ-SRN12N
deviceProfileName: basic-alarm
- id: "MultIR/MIR-SR100"
deviceLabel: MultiIR Siren MIR-SR100
manufacturer: MultIR
model: MIR-SR100
deviceProfileName: switch-alarm-tamper-warningduration-volume-no-fw-update
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: switch-alarm-tamper-warningduration-volume-no-fw-update
components:
- id: main
capabilities:
- id: switch
version: 1
- id: alarm
version: 1
- id: tamperAlert
version: 1
- id: refresh
version: 1
categories:
- name: Siren
preferences:
- title: "警报时长/秒(warning duration/sec)"
name: warningDuration
description: "警报持续时间 单位:秒(warning duration unit:seconds)"
required: false
preferenceType: integer
definition:
minimum: 30
maximum: 1800
default: 1800
- title: "警报音量(siren volume)"
name: sirenVolume
description: "警报音量大小(siren volume)"
required: false
preferenceType: enumeration
definition:
options:
0: "低(low)"
1: "中(medium)"
2: "高(high)"
3: "最大(max)"
default: 3
13 changes: 13 additions & 0 deletions drivers/SmartThings/zigbee-siren/src/MultiIR/can_handle.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Copyright 2026 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0

return function(opts, driver, device, ...)
local FINGERPRINTS = require "MultiIR.fingerprints"
for _, fingerprint in ipairs(FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
local subdriver = require("MultiIR")
return true, subdriver
end
end
return false
end
6 changes: 6 additions & 0 deletions drivers/SmartThings/zigbee-siren/src/MultiIR/fingerprints.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- Copyright 2026 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0

return {
{ mfr = "MultIR", model = "MIR-SR100" }
}
119 changes: 119 additions & 0 deletions drivers/SmartThings/zigbee-siren/src/MultiIR/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
-- Copyright 2026 SmartThings, Inc.
-- Licensed under the Apache License, Version 2.0


local data_types = require "st.zigbee.data_types"
local zcl_clusters = require "st.zigbee.zcl.clusters"

local IASWD = zcl_clusters.IASWD
local IASZone = zcl_clusters.IASZone
local IaswdLevel = IASWD.types.IaswdLevel
local SirenConfiguration = IASWD.types.SirenConfiguration
local WarningMode = IASWD.types.WarningMode
local Strobe = IASWD.types.Strobe
local capabilities = require "st.capabilities"
local ALARM_COMMAND = "alarmCommand"
local DEFAULT_MAX_WARNING_DURATION = 1800
local ALARM_STROBE_DUTY_CYCLE = 40

local alarm_command = {
OFF = 0,
SIREN = 1,
STROBE = 2,
BOTH = 3
}

local function device_added (driver, device)
device:emit_event(capabilities.switch.switch.off())
device:emit_event(capabilities.alarm.alarm.off())
if(device:supports_capability(capabilities.tamperAlert)) then
device:emit_event(capabilities.tamperAlert.tamper.clear())
end
device:send(IASWD.attributes.MaxDuration:read(device))
end

local function generate_event_from_zone_status(driver, device, zone_status, zb_rx)
if device:supports_capability(capabilities.tamperAlert) then
device:emit_event(zone_status:is_tamper_set() and capabilities.tamperAlert.tamper.detected() or capabilities.tamperAlert.tamper.clear())
end
end

local function ias_zone_status_change_handler(driver, device, zb_rx)
local zone_status = zb_rx.body.zcl_body.zone_status
generate_event_from_zone_status(driver, device, zone_status, zb_rx)
end

local function send_siren_command(device, warning_mode, warning_siren_level, warning_duration, strobe_active, strobe_level)
local siren_configuration

siren_configuration = SirenConfiguration(0x00)
siren_configuration:set_warning_mode(warning_mode)
siren_configuration:set_siren_level(warning_siren_level)
siren_configuration:set_strobe(strobe_active)

device:send(
IASWD.server.commands.StartWarning(
device,
siren_configuration,
data_types.Uint16(warning_duration),
data_types.Uint8(ALARM_STROBE_DUTY_CYCLE),
data_types.Enum8(strobe_level)
)
)
end

local function siren_switch_off_handler(driver, device, command)
device:set_field(ALARM_COMMAND, alarm_command.OFF, {persist = true})
send_siren_command(device, WarningMode.STOP, IaswdLevel.LOW_LEVEL, DEFAULT_MAX_WARNING_DURATION, Strobe.NO_STROBE, IaswdLevel.LOW_LEVEL)
end

local function siren_alarm_siren_handler(alarm_cmd, WarningMode, Strobe, strobe_level)
return function(driver, device, command)
device:set_field(ALARM_COMMAND, alarm_cmd, {persist = true})

local sirenVolume_msg = tonumber(device.preferences.sirenVolume) or IaswdLevel.VERY_HIGH_LEVEL
local warning_duration = tonumber(device.preferences.warningDuration) or DEFAULT_MAX_WARNING_DURATION

send_siren_command(device, WarningMode, sirenVolume_msg, warning_duration, Strobe, strobe_level)

device.thread:call_with_delay(warning_duration, function() -- Send command to switch from siren to off in the app when the siren is done
if(device:get_field(ALARM_COMMAND) ~= alarm_command.OFF) then
siren_switch_off_handler(driver, device, alarm_cmd)
end
end)
end
end

local MultiIR_siren_driver = {
NAME = "MultiIR Zigbee siren",
lifecycle_handlers = {
added = device_added,
},
capability_handlers = {
[capabilities.alarm.ID] = {
[capabilities.alarm.commands.off.NAME] = siren_switch_off_handler,
[capabilities.alarm.commands.siren.NAME] = siren_alarm_siren_handler(alarm_command.SIREN, WarningMode.BURGLAR, Strobe.NO_STROBE, IaswdLevel.LOW_LEVEL),
[capabilities.alarm.commands.both.NAME] = siren_alarm_siren_handler(alarm_command.BOTH, WarningMode.BURGLAR, Strobe.USE_STROBE , IaswdLevel.VERY_HIGH_LEVEL),
[capabilities.alarm.commands.strobe.NAME] = siren_alarm_siren_handler(alarm_command.STROBE, WarningMode.STOP, Strobe.USE_STROBE, IaswdLevel.VERY_HIGH_LEVEL)
},
[capabilities.switch.ID] = {
[capabilities.switch.commands.on.NAME] = siren_alarm_siren_handler(alarm_command.BOTH, WarningMode.BURGLAR, Strobe.USE_STROBE , IaswdLevel.VERY_HIGH_LEVEL),
[capabilities.switch.commands.off.NAME] = siren_switch_off_handler
}
},
zigbee_handlers = {
cluster = {
[IASZone.ID] = {
[IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler
}
},
attr = {
[IASZone.ID] = {
[IASZone.attributes.ZoneStatus.ID] = generate_event_from_zone_status
}
}
},
can_handle = require("MultiIR.can_handle"),
}

return MultiIR_siren_driver
1 change: 1 addition & 0 deletions drivers/SmartThings/zigbee-siren/src/sub_drivers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ local lazy_load_if_possible = require "lazy_load_subdriver"
return {
lazy_load_if_possible("frient"),
lazy_load_if_possible("ozom"),
lazy_load_if_possible("MultiIR"),
}
Loading
Loading