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
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
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
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