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
Original file line number Diff line number Diff line change
Expand Up @@ -365,18 +365,26 @@ local function open_action(source)
local recv_as_num = tonumber(recv, 16)

if recv_as_num ~= nil then
recv, err, partial = source._sock:receive(recv_as_num)
if err then
if err == "timeout" or err == "wantread" then
return
else
--- real error, close the connection.
if source._sock ~= nil then
source._sock:close()
source._sock = nil
-- A chunk size of 0 signals the end of the chunked transfer. Some
-- versions of the underlying socket library will raise a hard error
-- (rather than returning a graceful nil/"closed" pair) if we ask to
-- receive 0 bytes, so avoid calling receive() in that case entirely.
if recv_as_num == 0 then
recv = ""
else
recv, err, partial = source._sock:receive(recv_as_num)
if err then
if err == "timeout" or err == "wantread" then
return
else
--- real error, close the connection.
if source._sock ~= nil then
source._sock:close()
source._sock = nil
end
source.ready_state = EventSource.ReadyStates.CLOSED
return nil, err, partial
end
source.ready_state = EventSource.ReadyStates.CLOSED
return nil, err, partial
end
end
local _, err, partial = source._sock:receive('*l') -- clear the final line
Expand Down
5 changes: 5 additions & 0 deletions drivers/SmartThings/jbl/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ local function update_connection(driver, device, device_ip, device_info)
local conn_info = driver.discovery_helper.get_connection_info(driver, device_dni, device_ip, device_info)

local credential = device:get_field(fields.CREDENTIAL)
if not credential then
log.error("update_connection : failed to find credential, dni = " .. tostring(device_dni))
device:offline()
return
end

conn_info:add_header(CREDENTIAL_KEY_HEADER, credential)

Expand Down
2 changes: 1 addition & 1 deletion drivers/SmartThings/jbl/src/jbl/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function jbl_api.new_device_manager(bridge_ip, bridge_info, socket_builder)
end

function jbl_api:add_header(key, value)
log.info("add_header : " .. key .. ", " .. value)
log.info("add_header : " .. tostring(key) .. ", " .. tostring(value))
self.headers[key] = value
end

Expand Down
4 changes: 2 additions & 2 deletions drivers/SmartThings/matter-lock/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,10 @@ local function lock_state_handler(driver, device, ib, response)
-- In this case, two events occur. To prevent this, when both functions are called,
-- it send the event after 1 second so that no event occurs in the lock_state_handler.
device.thread:call_with_delay(1, function ()
if ib.data.value ~= nil then
if ib.data.value ~= nil and LOCK_STATE[ib.data.value] ~= nil then
device:emit_event(LOCK_STATE[ib.data.value])
else
device.log.warn("Lock State is nil")
device.log.warn(string.format("Received unknown Lock State: %s", ib.data.value))
end
end)
end
Expand Down
10 changes: 8 additions & 2 deletions drivers/SmartThings/zigbee-range-extender/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


local capabilities = require "st.capabilities"
local log = require "log"
local defaults = require "st.zigbee.defaults"
local Basic = (require "st.zigbee.zcl.clusters").Basic
local ZigbeeDriver = require "st.zigbee"
Expand Down Expand Up @@ -33,8 +34,13 @@ local zigbee_range_extender_driver = ZigbeeDriver("zigbee-range-extender", zigbe
function zigbee_range_extender_driver:device_health_check()
local device_list = self.device_api.get_device_list()
for _, device_id in ipairs(device_list) do
local device = self:get_device_info(device_id, false)
device:send(Basic.attributes.ZCLVersion:read(device))
local device, err = self:get_device_info(device_id, false)
if device == nil then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we request the device list immediately before this, I'm not really sure how this is possible. This one might be worth grabbing hub logs for some hubs to investigate because it seems like it shouldn't be possible to me.

@cjswedes cjswedes Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can happen if there is an incomplete inventory record for the device. Worth looking into though, like you mention

log.warn_with({ hub_logs = true },
string.format("device_health_check failed to get device info for device_id %s: %s", device_id, err))
else
device:send(Basic.attributes.ZCLVersion:read(device))
end
end
end

Expand Down
27 changes: 21 additions & 6 deletions drivers/SmartThings/zigbee-switch/src/inovelli/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,11 @@ local function huePercentToValue(value)
else return utils.round(value / 100 * 255) end
end

local function getNotificationValue(device, value)
local function getNotificationValue(device, parent, value)
local notificationValue = 0
local level = device:get_latest_state("main", capabilities.switchLevel.ID, capabilities.switchLevel.level.NAME) or 100
local color = utils.round(device:get_latest_state("main", capabilities.colorControl.ID, capabilities.colorControl.hue.NAME) or 100)
local effect = device:get_parent_device().preferences.notificationType or 1
local effect = (parent and parent.preferences.notificationType) or 1
notificationValue = notificationValue + (effect*16777216)
notificationValue = notificationValue + (huePercentToValue(value or color)*65536)
notificationValue = notificationValue + (level*256)
Expand All @@ -276,13 +276,16 @@ local function on_handler(driver, device, command)
else
device:emit_event(capabilities.switch.switch("on"))
local dev = device:get_parent_device()
if dev == nil then
return
end
local send_configuration = function()
dev:send(cluster_base.build_manufacturer_specific_command(
dev,
PRIVATE_CLUSTER_ID,
PRIVATE_CMD_NOTIF_ID,
MFG_CODE,
utils.serialize_int(getNotificationValue(device),4,false,false)))
utils.serialize_int(getNotificationValue(device, dev),4,false,false)))
end
device.thread:call_with_delay(1,send_configuration)
end
Expand All @@ -294,6 +297,9 @@ local function on_handler(driver, device, command)
else
device:emit_event(capabilities.switch.switch("off"))
local dev = device:get_parent_device()
if dev == nil then
return
end
local send_configuration = function()
dev:send(cluster_base.build_manufacturer_specific_command(
dev,
Expand All @@ -313,13 +319,16 @@ local function switch_level_handler(driver, device, command)
device:emit_event(capabilities.switchLevel.level(command.args.level))
device:emit_event(capabilities.switch.switch(command.args.level ~= 0 and "on" or "off"))
local dev = device:get_parent_device()
if dev == nil then
return
end
local send_configuration = function()
dev:send(cluster_base.build_manufacturer_specific_command(
dev,
PRIVATE_CLUSTER_ID,
PRIVATE_CMD_NOTIF_ID,
MFG_CODE,
utils.serialize_int(getNotificationValue(device),4,false,false)))
utils.serialize_int(getNotificationValue(device, dev),4,false,false)))
end
device.thread:call_with_delay(1,send_configuration)
end
Expand All @@ -330,13 +339,16 @@ local function set_color_temperature(driver, device, command)
device:emit_event(capabilities.colorTemperature.colorTemperature(command.args.temperature))
device:emit_event(capabilities.switch.switch("on"))
local dev = device:get_parent_device()
if dev == nil then
return
end
local send_configuration = function()
dev:send(cluster_base.build_manufacturer_specific_command(
dev,
PRIVATE_CLUSTER_ID,
PRIVATE_CMD_NOTIF_ID,
MFG_CODE,
utils.serialize_int(getNotificationValue(device, 100),4,false,false)))
utils.serialize_int(getNotificationValue(device, dev, 100),4,false,false)))
end
device.thread:call_with_delay(1,send_configuration)
end
Expand All @@ -346,13 +358,16 @@ local function set_color_temperature(driver, device, command)
device:emit_event(capabilities.colorControl.saturation(command.args.color.saturation))
device:emit_event(capabilities.switch.switch("on"))
local dev = device:get_parent_device()
if dev == nil then
return
end
local send_configuration = function()
dev:send(cluster_base.build_manufacturer_specific_command(
dev,
PRIVATE_CLUSTER_ID,
PRIVATE_CMD_NOTIF_ID,
MFG_CODE,
utils.serialize_int(getNotificationValue(device),4,false,false)))
utils.serialize_int(getNotificationValue(device, dev),4,false,false)))
end
device.thread:call_with_delay(1,send_configuration)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ local function handle_tamper_event(driver, device, cmd)
device.thread:cancel_timer(tamper_timer)
end
device:set_field(TAMPER_TIMER, device.thread:call_with_delay(TAMPER_CLEAR, function()
-- The device can be removed while this timer is still pending. Once a device is
-- deleted its metatable is swapped so that all field/method lookups return nil
-- (see Device:deleted() in st.device), so guard against that here to avoid
-- "attempt to call a nil value (method 'emit_event_for_endpoint')" errors.
if device.id == nil then
return
end
device:emit_event_for_endpoint(cmd.src_channel, capabilities.tamperAlert.tamper.clear())
device:set_field(TAMPER_TIMER, nil)
end))
Expand Down
Loading