Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hue: Fix multi-component device initialization race condition #1831

Merged
merged 3 commits into from
Dec 19, 2024
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
3 changes: 3 additions & 0 deletions drivers/SmartThings/philips-hue/src/disco/button.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local log = require "log"
local socket = require "cosock".socket
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local HueDeviceTypes = require "hue_device_types"

Expand Down
3 changes: 3 additions & 0 deletions drivers/SmartThings/philips-hue/src/disco/contact.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local log = require "log"
local socket = require "cosock".socket
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local HueDeviceTypes = require "hue_device_types"

Expand Down
3 changes: 3 additions & 0 deletions drivers/SmartThings/philips-hue/src/disco/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ local socket = require "cosock.socket"
local mdns = require "st.mdns"
local net_utils = require "st.net_utils"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Fields = require "fields"
local HueApi = require "hue.api"
Expand Down
3 changes: 3 additions & 0 deletions drivers/SmartThings/philips-hue/src/disco/light.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local log = require "log"
local socket = require "cosock".socket
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local HueDeviceTypes = require "hue_device_types"

Expand Down
3 changes: 3 additions & 0 deletions drivers/SmartThings/philips-hue/src/disco/motion.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local log = require "log"
local socket = require "cosock".socket
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local HueDeviceTypes = require "hue_device_types"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ local capabilities = require "st.capabilities"
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: table, name: string?, multi_line: boolean?): string
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Consts = require "consts"
Expand Down
5 changes: 4 additions & 1 deletion drivers/SmartThings/philips-hue/src/handlers/commands.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Consts = require "consts"
local Fields = require "fields"
Expand All @@ -8,7 +11,7 @@ local HueColorUtils = require "utils.cie_utils"
local utils = require "utils"

-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: table, name: string?, multi_line: boolean?): string
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

---@class CommandHandlers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local capabilities = require "st.capabilities"
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local refresh_handler = require("handlers.commands").refresh_handler

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local refresh_handler = require("handlers.commands").refresh_handler

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local capabilities = require "st.capabilities"

local Discovery = require "disco"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ local log = require "log"
local capabilities = require "st.capabilities"
local refresh_handler = require("handlers.commands").refresh_handler
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Consts = require "consts"
local Discovery = require "disco"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local refresh_handler = require("handlers.commands").refresh_handler

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local capabilities = require "st.capabilities"
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Discovery = require "disco"
local Fields = require "fields"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local cosock = require "cosock"
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Fields = require "fields"
local HueDeviceTypes = require "hue_device_types"
Expand Down Expand Up @@ -123,6 +126,7 @@ function RefreshHandlers.do_refresh_all_for_bridge(driver, bridge_device)
-- but only the first time we encounter a device type. We cache them since we're refreshing
-- everything.
if
device_type and
type(device_type_refresh_handlers_map[device_type]) == "function" and
statuses_by_device_type[device_type] == nil
then
Expand Down
10 changes: 9 additions & 1 deletion drivers/SmartThings/philips-hue/src/hue/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ local json = require "st.json"
local log = require "log"
local RestClient = require "lunchbox.rest"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local HueDeviceTypes = require "hue_device_types"

-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: table, name: string?, multi_line: boolean?): string
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local APPLICATION_KEY_HEADER = "hue-application-key"
Expand Down Expand Up @@ -82,6 +85,7 @@ end
---@return table? tbl the table representation of the JSON response, nil on error
---@return string? err the error message, nil on success
---@return string? partial the partial response if the response was not complete
---@return ...
local function process_rest_response(response, err, partial, err_callback)
if err == nil and response == nil then
log.error_with({ hub_logs = true },
Expand Down Expand Up @@ -208,6 +212,7 @@ end
---@param path string
---@return table|nil response REST response, nil if error
---@return nil|string error nil on success
---@return ...
local function do_get(instance, path)
local reply_tx, reply_rx = channel.new()
reply_rx:settimeout(10)
Expand All @@ -226,6 +231,7 @@ end
---@param payload string
---@return table|nil response REST response, nil if error
---@return nil|string error nil on success
---@return ...
local function do_put(instance, path, payload)
local reply_tx, reply_rx = channel.new()
reply_rx:settimeout(10)
Expand All @@ -244,6 +250,7 @@ end
---@return HueBridgeInfo|nil bridge_info nil on err
---@return nil|string error nil on success
---@return nil|string partial partial response if available, nil otherwise
---@return ...
function PhilipsHueApi.get_bridge_info(bridge_ip, socket_builder)
local tx, rx = channel.new()
rx:settimeout(10)
Expand All @@ -266,6 +273,7 @@ end
---@return HueApiKeyResponse[]? api_key_response nil on err
---@return string? error nil on success
---@return string? partial partial response if available, nil otherwise
---@return ...
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: what is this indicating, that the function could return any number of additional any typed arguments? What is the advantage of adding this instead of enumerating the actual return types after the two know error values?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was to make the typechecker happy. The actual unpack here should stop at the three things indicated, but the signature of table.unpack in LuaLS's meta typedefs indicates up to 12 return values.

function PhilipsHueApi.request_api_key(bridge_ip, socket_builder)
local tx, rx = channel.new()
rx:settimeout(10)
Expand Down
3 changes: 3 additions & 0 deletions drivers/SmartThings/philips-hue/src/hue_debug/init.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local log = require "log"
local utils = require "utils"
Expand Down
8 changes: 6 additions & 2 deletions drivers/SmartThings/philips-hue/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
-- Improvements to be made:
--
-- ===============================================================================================
local Driver = require "st.driver"
local logjam = require "logjam"
logjam.enable_passthrough()
logjam.inject_global()

local log = require "log"

local Driver = require "st.driver"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: table, name: string?, multi_line: boolean?): string
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Discovery = require "disco"
Expand Down
108 changes: 101 additions & 7 deletions drivers/SmartThings/philips-hue/src/logjam.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,84 @@
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local logjam = {}

logjam.real_log = log.log

logjam.enabled_modules = {}

function logjam.inject_global()
for field_key, level_key in pairs(log) do
if
string.find(field_key, "LOG_LEVEL_") and
type(level_key) == "string" and
type(log[level_key]) == "function"
then
log[level_key] = logjam[level_key]
local level_with_key = string.format("%s_with", level_key)
if type(log[level_with_key]) == "function" then
log[level_with_key] = logjam[level_with_key]
end
end
end
log.log = logjam.log
end

function logjam.enable_passthrough()
logjam.passthrough = true
end

function logjam.disable_passthrough()
logjam.passthrough = false
end

function logjam.enable(module)
logjam.enabled_modules[module] = true
end

function logjam.disable(module)
logjam.enabled_modules[module] = nil
end

function logjam.log(opts, level, ...)
if opts.on == true then
log.log(opts, level, ...)
local call_info
if not opts.call_info then
call_info = debug.getinfo(2)
else
call_info = opts.call_info
end
opts.call_info = nil

local module_name = nil
if type(call_info.source) == "string" then
module_name =
call_info.source
:gsub("%.lua", "")
:gsub("/init", "")
:gsub("/", ".")
:gsub("^init$", "philips-hue")
end

local module_enabled = false
local module_prefix = ""
if type(module_name) == "string" and module_name:len() > 0 then
module_enabled = logjam.enabled_modules[module_name]
module_prefix = string.format("[%s] ", module_name)
end

-- explicit on/off log option takes precedence, so that we can allow
-- `false` to override passthrough/module_enabled flags.
if type(opts.on) == "boolean" then
if opts.on then
logjam.real_log(opts, level, module_prefix, ...)
end
return
end
if logjam.passthrough or module_enabled then
logjam.real_log(opts, level, module_prefix, ...)
end
end

Expand All @@ -16,16 +91,35 @@ for field_key, level_key in pairs(log) do
local level_with_key = string.format("%s_with", level_key)
logjam[level_key] = function(...)
local first_arg = select(1, ...)
if first_arg == true or (type(first_arg) == "table" and first_arg.on == true) then
log[level_key](select(2, ...))
local opts = {}
local log_args_start_idx = 1
if type(first_arg) == "boolean" then
opts.on = first_arg
log_args_start_idx = 2
elseif type(first_arg) == "table" then
opts = first_arg
log_args_start_idx = 2
end
local info = debug.getinfo(2)
opts.call_info = info
logjam.log(opts, level_key, select(log_args_start_idx, ...))
end

logjam[level_with_key] = function(opts, ...)
opts = opts or {}
if opts.on == true then
log[level_with_key](...)
local log_opts = {}
local log_args = table.pack(...)
if type(opts) == "table" then
for k, v in pairs(opts) do
log_opts[k] = v
end
elseif type(opts) == "boolean" then
log_opts.on = opts
elseif opts ~= nil then
log_args.insert(log_args, 1, opts)
end
local info = debug.getinfo(2)
log_opts.call_info = info
logjam.log(log_opts, level_key, table.unpack(log_args))
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ local ssl = require "cosock.ssl"
---@type fun(sock: table, config: table?): table?, string?
ssl.wrap = ssl.wrap

local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local log = require "log"
local util = require "lunchbox.util"
local Request = require "luncheon.request"
Expand Down Expand Up @@ -474,7 +479,6 @@ function EventSource.new(url, extra_headers, sock_builder)
}, EventSource)

cosock.spawn(function()
local st_utils = require "st.utils"
while true do
if source.ready_state == EventSource.ReadyStates.CLOSED and
not source._reconnect
Expand Down
3 changes: 3 additions & 0 deletions drivers/SmartThings/philips-hue/src/stray_device_helper.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
local cosock = require "cosock"
local log = require "log"
local st_utils = require "st.utils"
-- trick to fix the VS Code Lua Language Server typechecking
---@type fun(val: any?, name: string?, multi_line: boolean?): string
st_utils.stringify_table = st_utils.stringify_table

local Discovery = require "disco"
local Fields = require "fields"
Expand Down
Loading
Loading