Skip to content
8 changes: 7 additions & 1 deletion drivers/SmartThings/matter-lock/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ local function lock_state_handler(driver, device, ib, response)
}

if ib.data.value ~= nil then
device:emit_event(LOCK_STATE[ib.data.value])
local event = LOCK_STATE[ib.data.value]
if event ~= nil then
device:emit_event(event)
else
device.log.warn(string.format("Received unknown lock state value (%s), emitting unknown", ib.data.value))
device:emit_event(attr.unknown())
end
else
device:emit_event(LOCK_STATE[LockState.NOT_FULLY_LOCKED])
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ local NEW_MATTER_LOCK_PRODUCTS = {
{0x115f, 0x2807}, -- AQARA, U200 Lite
{0x115f, 0x2804}, -- AQARA, U400
{0x115f, 0x286A}, -- AQARA, U200 US
{0x115f, 0x2805}, -- Aqara Smart Lock J200 Set
{0x115f, 0x280e}, -- AQARA Smart Gate Lock U500
{0x115f, 0x280f}, -- AQARA Smart Rim Lock U500
{0x115f, 0x2810}, -- AQARA Smart Glass Door Lock U500
{0x147F, 0x0001}, -- U-tec
{0x147F, 0x0007}, -- ULTRALOQ Bolt Pro Smart Matter Door Lock
{0x147F, 0x0008}, -- Ultraloq, Bolt Smart Matter Door Lock
Expand Down
23 changes: 23 additions & 0 deletions drivers/SmartThings/matter-lock/src/test/test_matter_lock.lua
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,29 @@ test.register_message_test(
}
)

test.register_message_test(
"Handle unknown LockState value from Matter device.", {
{
channel = "matter",
direction = "receive",
message = {
mock_device.id,
clusters.DoorLock.attributes.LockState:build_test_report_data(
mock_device, 10, 0xFF
),
},
},
{
channel = "capability",
direction = "send",
message = mock_device:generate_test_message("main", capabilities.lock.lock.unknown()),
},
},
{
min_api_version = 17
}
)

test.register_message_test(
"Handle received BatPercentRemaining from device.", {
{
Expand Down
10 changes: 10 additions & 0 deletions drivers/SmartThings/matter-switch/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,11 @@ matterManufacturer:
vendorId: 0x1339
productId: 0x0016
deviceProfileName: light-color-level-2000K-7000K
- id: "4921/86"
deviceLabel: Cync Smart Shop Light
vendorId: 0x1339
productId: 0x0056
deviceProfileName: light-color-level
#Govee
- id: "4999/24740"
deviceLabel: Govee Square Ceiling Light (12 inch)
Expand Down Expand Up @@ -4149,6 +4154,11 @@ matterManufacturer:
vendorId: 0x1392
productId: 0x010D
deviceProfileName: plug-binary
- id: "20496/610"
deviceLabel: Mini Smart Wi-Fi Plug Energy Monitoring Matter
vendorId: 0x5010
productId: 0x0262
deviceProfileName: plug-power-energy-powerConsumption


matterGeneric:
Expand Down
33 changes: 25 additions & 8 deletions drivers/SmartThings/samsung-audio/src/command.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ local function is_empty(t)
return not t or (type(t) == "table" and #t == 0)
end

local function get_uic_response(ret, command_name)
local root = ret and ret.handler_res and ret.handler_res.root
local uic = root and root.UIC
local response = uic and uic.response

if not uic then
log.warn(string.format("Missing UIC data in %s response", tostring(command_name)))
return nil, nil
end

return uic, response
end

local function tr(s,mappings)
return string.gsub(s,
"(.)",
Expand Down Expand Up @@ -94,8 +107,9 @@ function Command.volume(ip)
if ip then
local url = format_url(ip, "/UIC?cmd=<name>GetVolume</name>")
local ret = handle_http_request(ip, url)
if ret then
response_map = { volume = ret.handler_res.root.UIC.response.volume, }
local _, response = get_uic_response(ret, "GetVolume")
if response and response.volume ~= nil then
response_map = { volume = response.volume, }
end
end
return response_map
Expand All @@ -114,8 +128,9 @@ function Command.set_volume(ip, level)
local encoded_str_vol = "/UIC?cmd=%3Cpwron%3Eon%3C/pwron%3E%3Cname%3ESetVolume%3C/name%3E%3Cp%20type=%22dec%22%20name=%22volume%22%20val=%22" .. level .. "%22%3E%3C/p%3E"
local url = format_url(ip, encoded_str_vol)
local ret = handle_http_request(ip, url)
if ret then
response_map = { volume = ret.handler_res.root.UIC.response.volume, }
local _, response = get_uic_response(ret, "SetVolume")
if response and response.volume ~= nil then
response_map = { volume = response.volume, }
end
end
return response_map
Expand Down Expand Up @@ -326,8 +341,9 @@ function Command.getMute(ip)
if ip then
local url = format_url(ip, "/UIC?cmd=<name>GetMute</name>")
local ret = handle_http_request(ip, url)
if ret then
response_map = { muted = ret.handler_res.root.UIC.response.mute,}
local _, response = get_uic_response(ret, "GetMute")
if response and response.mute ~= nil then
response_map = { muted = response.mute,}
end
end
return response_map
Expand All @@ -342,8 +358,9 @@ function Command.getPlayStatus(ip)
if ip then
local url = format_url(ip, "/UIC?cmd=<name>GetPlayStatus</name>")
local ret = handle_http_request(ip, url)
if ret then
response_map = { playstatus = ret.handler_res.root.UIC.response.playstatus,}
local _, response = get_uic_response(ret, "GetPlayStatus")
if response and response.playstatus ~= nil then
response_map = { playstatus = response.playstatus,}
end
end
return response_map
Expand Down
2 changes: 1 addition & 1 deletion drivers/SmartThings/samsung-audio/src/handlers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ end
function CapabilityHandlers.handle_audio_notification(driver, device, cmd)
local ip = device:get_field("ip")
local mute_status = command.getMute(ip)
if mute_status.muted ~= "off" then
if mute_status and mute_status.muted ~= nil and mute_status.muted ~= "off" then
--unmute before playig notification
command.unmute(ip)
end
Expand Down
17 changes: 13 additions & 4 deletions drivers/SmartThings/samsung-audio/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,23 @@ local function emit_refresh_data_to_server(driver, device, cmd)

-- get volume
local vol = command.volume(device:get_field("ip"))
device:emit_event(capabilities.audioVolume.volume(tonumber(vol.volume)))
local current_volume = vol and tonumber(vol.volume)
if current_volume ~= nil then
device:emit_event(capabilities.audioVolume.volume(current_volume))
else
log.warn("Unable to read speaker volume from refresh response")
end

-- get mute status
local muteStatus = command.getMute(device:get_field("ip"))
if muteStatus.muted ~= "off" then
device:emit_event(capabilities.audioMute.mute.muted())
if muteStatus and muteStatus.muted ~= nil then
if muteStatus.muted ~= "off" then
device:emit_event(capabilities.audioMute.mute.muted())
else
device:emit_event(capabilities.audioMute.mute.unmuted())
end
else
device:emit_event(capabilities.audioMute.mute.unmuted())
log.warn("Unable to read speaker mute state from refresh response")
end
end

Expand Down
65 changes: 46 additions & 19 deletions drivers/SmartThings/zigbee-switch/src/zll-polling/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,63 @@ local device_lib = require "st.device"
local clusters = require "st.zigbee.zcl.clusters"
local configurationMap = require "configurations"

local function set_up_zll_polling(driver, device)
local INFREQUENT_POLL_COUNTER = "_infrequent_poll_counter"
local function poll()
local infrequent_counter = device:get_field(INFREQUENT_POLL_COUNTER) or 1
if infrequent_counter == 12 then
-- do a full refresh once an hour
device:refresh()
infrequent_counter = 0
else
-- Read On/Off every poll
for _, ep in pairs(device.zigbee_endpoints) do
if device:supports_server_cluster(clusters.OnOff.ID, ep.id) then
device:send(clusters.OnOff.attributes.OnOff:read(device):to_endpoint(ep.id))
end
local INFREQUENT_POLL_COUNTER = "_infrequent_poll_counter"
local ZLL_POLL_TIMER = "_zll_poll_timer"

local function do_zll_poll(device)
if device == nil or type(device.get_field) ~= "function" then
return
end

local infrequent_counter = device:get_field(INFREQUENT_POLL_COUNTER) or 1
if infrequent_counter == 12 then
-- do a full refresh once an hour
device:refresh()
infrequent_counter = 0
else
-- Read On/Off every poll
for _, ep in pairs(device.zigbee_endpoints) do
if device:supports_server_cluster(clusters.OnOff.ID, ep.id) then
device:send(clusters.OnOff.attributes.OnOff:read(device):to_endpoint(ep.id))
end
infrequent_counter = infrequent_counter + 1
end
device:set_field(INFREQUENT_POLL_COUNTER, infrequent_counter)
infrequent_counter = infrequent_counter + 1
end
device:set_field(INFREQUENT_POLL_COUNTER, infrequent_counter)
end

local function set_up_zll_polling(driver, device)
-- only set this up for non-child devices
if device.network_type == device_lib.NETWORK_TYPE_ZIGBEE then
device.thread:call_on_schedule(5 * 60, poll, "zll_polling")
if device.network_type ~= device_lib.NETWORK_TYPE_ZIGBEE then
return
end

-- should never happen, but defensive check
local existing_timer = device:get_field(ZLL_POLL_TIMER)
if existing_timer ~= nil then
device.thread:cancel_timer(existing_timer)
end

local timer = device.thread:call_on_schedule(5 * 60, function()
do_zll_poll(device)
end, "zll_polling")

device:set_field(ZLL_POLL_TIMER, timer)
end

local function remove_zll_polling(driver, device)
local existing_timer = device:get_field(ZLL_POLL_TIMER)
if existing_timer ~= nil then
device.thread:cancel_timer(existing_timer)
device:set_field(ZLL_POLL_TIMER, nil)
end
end

local ZLL_polling = {
NAME = "ZLL Polling",
lifecycle_handlers = {
init = configurationMap.reconfig_wrapper(set_up_zll_polling)
init = configurationMap.reconfig_wrapper(set_up_zll_polling),
removed = remove_zll_polling
},
can_handle = require("zll-polling.can_handle"),
}
Expand Down
4 changes: 3 additions & 1 deletion drivers/SmartThings/zigbee-valve/src/sinope/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ local PowerConfiguration = clusters.PowerConfiguration
local function device_init(driver, device)
battery_defaults.use_battery_voltage_handling(device)
-- according to the DTH, this attribute cannot be configured for reporting
device.thread:call_on_schedule(900, function() device:send(PowerConfiguration.attributes.BatteryVoltage:read()) end)
device.thread:call_on_schedule(900, function()
device:send(PowerConfiguration.attributes.BatteryVoltage:read(device))
end)
end

local function battery_voltage_handler(driver, device, command)
Expand Down
Loading