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
7 changes: 6 additions & 1 deletion drivers/SmartThings/philips-hue/src/disco/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,13 @@ function HueDiscovery.handle_discovered_child_device(driver, bridge_network_id,
end

for _, svc_info in ipairs(primary_services[primary_service_type]) do
if driver:get_device_by_dni(v1_dni) then
return
end
local v2_resource_id = svc_info.rid or ""
if driver:get_device_by_dni(v1_dni) or driver.hue_identifier_to_device_record[v2_resource_id] then return end
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 is what is currently gating us from creating the devices, specifically the last part of the conditional

if (driver.hue_identifier_to_device_record_by_bridge[bridge_network_id] or {})[v2_resource_id] then
return
end
end

local api_instance =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ function ButtonLifecycleHandlers.added(driver, device, parent_device_id, resourc
end

local button_rid_to_index_map = {}
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
if button_info.button then
driver.hue_identifier_to_device_record[button_info.id] = device
hue_id_to_device[button_info.id] = device
button_rid_to_index_map[button_info.id] = 1
end

Expand All @@ -65,7 +66,7 @@ function ButtonLifecycleHandlers.added(driver, device, parent_device_id, resourc
local button_id = button_info[button_id_key]

if button and button_id then
driver.hue_identifier_to_device_record[button_id] = device
hue_id_to_device[button_id] = device
button_rid_to_index_map[button_id] = var

local supported_button_values = utils.get_supported_button_values(button.event_values)
Expand All @@ -86,7 +87,7 @@ function ButtonLifecycleHandlers.added(driver, device, parent_device_id, resourc
end

if button_info.power_id then
driver.hue_identifier_to_device_record[button_info.power_id] = device
hue_id_to_device[button_info.power_id] = device
end

log.debug(st_utils.stringify_table(button_rid_to_index_map, "button index map", true))
Expand All @@ -98,7 +99,7 @@ function ButtonLifecycleHandlers.added(driver, device, parent_device_id, resourc
device:set_field(Fields._ADDED, true, { persist = true })
device:set_field(Fields._REFRESH_AFTER_INIT, true, { persist = true })

driver.hue_identifier_to_device_record[device_button_resource_id] = device
hue_id_to_device[device_button_resource_id] = device
end

---@param driver HueDriver
Expand All @@ -114,8 +115,9 @@ function ButtonLifecycleHandlers.init(driver, device)
log.debug("resource id " .. tostring(device_button_resource_id))

local hue_device_id = device:get_field(Fields.HUE_DEVICE_ID)
if not driver.hue_identifier_to_device_record[device_button_resource_id] then
driver.hue_identifier_to_device_record[device_button_resource_id] = device
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
if not hue_id_to_device[device_button_resource_id] then
hue_id_to_device[device_button_resource_id] = device
end
local button_info, err
button_info = Discovery.device_state_disco_cache[device_button_resource_id]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ function ContactLifecycleHandlers.added(driver, device, parent_device_id, resour
return
end

driver.hue_identifier_to_device_record[sensor_info.power_id] = device
driver.hue_identifier_to_device_record[sensor_info.tamper_id] = device
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
hue_id_to_device[sensor_info.power_id] = device
hue_id_to_device[sensor_info.tamper_id] = device

device:set_field(Fields.DEVICE_TYPE, HueDeviceTypes.CONTACT, { persist = true })
device:set_field(Fields.HUE_DEVICE_ID, sensor_info.hue_device_id, { persist = true })
Expand All @@ -61,7 +62,7 @@ function ContactLifecycleHandlers.added(driver, device, parent_device_id, resour
device:set_field(Fields._ADDED, true, { persist = true })
device:set_field(Fields._REFRESH_AFTER_INIT, true, { persist = true })

driver.hue_identifier_to_device_record[device_sensor_resource_id] = device
hue_id_to_device[device_sensor_resource_id] = device
end

---@param driver HueDriver
Expand All @@ -77,8 +78,9 @@ function ContactLifecycleHandlers.init(driver, device)
log.debug("resource id " .. tostring(device_sensor_resource_id))

local hue_device_id = device:get_field(Fields.HUE_DEVICE_ID)
if not driver.hue_identifier_to_device_record[device_sensor_resource_id] then
driver.hue_identifier_to_device_record[device_sensor_resource_id] = device
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
if not hue_id_to_device[device_sensor_resource_id] then
hue_id_to_device[device_sensor_resource_id] = device
end
local sensor_info, err
sensor_info = Discovery.device_state_disco_cache[device_sensor_resource_id]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ function LifecycleHandlers.device_added(driver, device, ...)
if device_type ~= HueDeviceTypes.BRIDGE then
---@cast device HueChildDevice
local resource_id = utils.get_hue_rid(device)
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
if resource_id then
driver.hue_identifier_to_device_record[resource_id] = device
hue_id_to_device[resource_id] = device
end

local resource_state_known = (Discovery.device_state_disco_cache[resource_id] ~= nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ function LightLifecycleHandlers.added(driver, device, parent_device_id, resource
device:set_field(Fields._ADDED, true, { persist = true })
device:set_field(Fields._REFRESH_AFTER_INIT, true, { persist = true })

driver.hue_identifier_to_device_record[device_light_resource_id] = device
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
hue_id_to_device[device_light_resource_id] = device

-- the refresh handler adds lights that don't have a fully initialized bridge to a queue.
driver:inject_capability_command(device, {
Expand Down Expand Up @@ -218,8 +219,9 @@ function LightLifecycleHandlers.init(driver, device)
device.device_network_id

local hue_device_id = device:get_field(Fields.HUE_DEVICE_ID)
if not driver.hue_identifier_to_device_record[device_light_resource_id] then
driver.hue_identifier_to_device_record[device_light_resource_id] = device
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
if not hue_id_to_device[device_light_resource_id] then
hue_id_to_device[device_light_resource_id] = device
end
local svc_rids_for_device = driver.services_for_device_rid[hue_device_id] or {}
if not svc_rids_for_device[device_light_resource_id] then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ function MotionLifecycleHandlers.added(driver, device, parent_device_id, resourc
})
return
end
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}

driver.hue_identifier_to_device_record[sensor_info.power_id] = device
driver.hue_identifier_to_device_record[sensor_info.temperature_id] = device
driver.hue_identifier_to_device_record[sensor_info.light_level_id] = device
hue_id_to_device[sensor_info.power_id] = device
hue_id_to_device[sensor_info.temperature_id] = device
hue_id_to_device[sensor_info.light_level_id] = device

device:set_field(Fields.DEVICE_TYPE, HueDeviceTypes.MOTION, { persist = true })
device:set_field(Fields.HUE_DEVICE_ID, sensor_info.hue_device_id, { persist = true })
Expand All @@ -62,7 +63,7 @@ function MotionLifecycleHandlers.added(driver, device, parent_device_id, resourc
device:set_field(Fields._ADDED, true, { persist = true })
device:set_field(Fields._REFRESH_AFTER_INIT, true, { persist = true })

driver.hue_identifier_to_device_record[device_sensor_resource_id] = device
hue_id_to_device[device_sensor_resource_id] = device
end

---@param driver HueDriver
Expand All @@ -78,8 +79,9 @@ function MotionLifecycleHandlers.init(driver, device)
log.debug("resource id " .. tostring(device_sensor_resource_id))

local hue_device_id = device:get_field(Fields.HUE_DEVICE_ID)
if not driver.hue_identifier_to_device_record[device_sensor_resource_id] then
driver.hue_identifier_to_device_record[device_sensor_resource_id] = device
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device) or {}
if not hue_id_to_device[device_sensor_resource_id] then
hue_id_to_device[device_sensor_resource_id] = device
end
local sensor_info, err
sensor_info = Discovery.device_state_disco_cache[device_sensor_resource_id]
Expand Down
4 changes: 2 additions & 2 deletions drivers/SmartThings/philips-hue/src/hue_driver_template.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ local set_color_temp_handler = utils.safe_wrap_handler(command_handlers.set_colo
--- @class HueDriver:Driver
--- @field public ignored_bridges table<string,boolean>
--- @field public joined_bridges table<string,boolean>
--- @field public hue_identifier_to_device_record table<string,HueChildDevice>
--- @field public hue_identifier_to_device_record_by_bridge table<string, table<string,HueChildDevice>>
--- @field public services_for_device_rid table<string,table<string,string>> Map the device resource ID to another map that goes from service rid to service rtype
--- @field public waiting_grandchildren table<string,{ waiting_resource_info: HueResourceInfo, join_callback: fun(driver: HueDriver, waiting_resource_info: HueResourceInfo, parent_device: HueChildDevice)}[]>?
--- @field public stray_device_tx table cosock channel
Expand Down Expand Up @@ -113,7 +113,7 @@ function HueDriver.new_driver_template(dbg_config)

ignored_bridges = {},
joined_bridges = {},
hue_identifier_to_device_record = {},
hue_identifier_to_device_record_by_bridge = {},
services_for_device_rid = {},
-- the only real way we have to know which bridge a bulb wants to use at migration time
-- is by looking at the stored api key so we will make a map to look up bridge IDs with
Expand Down
24 changes: 17 additions & 7 deletions drivers/SmartThings/philips-hue/src/utils/grouped_utils.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local log = require "log"
local Fields = require "fields"
local cosock = require "cosock"
local utils = require "utils"

--- Room or zone with the children translated from their hue device id or light resource id to
--- their SmartThings represented device object. The grouped light resource id is also moved into
Expand Down Expand Up @@ -100,10 +101,11 @@ end
---@param group_kind string room or zone
---@param driver HueDriver
---@param hue_id_to_device table
---@param light_id_to_device table
---@param resp table?
---@param err any?
---@return HueLightGroup[]?
local function handle_group_scan_response(group_kind, driver, hue_id_to_device, resp, err)
local function handle_group_scan_response(group_kind, driver, hue_id_to_device, light_id_to_device, resp, err)
if err or not resp then
log.error(string.format("Failed to scan for %s: %s", group_kind, err or "unknown error"))
return nil
Expand All @@ -121,7 +123,7 @@ local function handle_group_scan_response(group_kind, driver, hue_id_to_device,

log.info(string.format("Successfully got %d %s", #resp.data, group_kind))
for _, group in ipairs(resp.data) do
build_hue_light_group(group, hue_id_to_device, driver.hue_identifier_to_device_record)
build_hue_light_group(group, hue_id_to_device, light_id_to_device)
log.info(string.format("Found light group %s with %d device records", group.id, #group.devices))
end

Expand All @@ -134,12 +136,14 @@ end
--- @param hue_id_to_device table
function grouped_utils.scan_groups(driver, bridge_device, api, hue_id_to_device)
local rooms, zones
while not (rooms and zones) do -- TODO: Should this be one and done? Timeout?
-- These are the hue light/other service ids rather than the hue device ids
local light_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, bridge_device) or {}
while not (rooms and zones) do
if not rooms then
rooms = handle_group_scan_response("rooms", driver, hue_id_to_device, api:get_rooms())
rooms = handle_group_scan_response("rooms", driver, hue_id_to_device, light_id_to_device, api:get_rooms())
end
if not zones then
zones = handle_group_scan_response("zones", driver, hue_id_to_device, api:get_zones())
zones = handle_group_scan_response("zones", driver, hue_id_to_device, light_id_to_device, api:get_zones())
end
end
-- Combine rooms and zones.
Expand Down Expand Up @@ -204,7 +208,10 @@ function grouped_utils.group_update(driver, bridge_device, to_update)
if to_update.children then
local devices =
build_group_device_table(
to_update, build_hue_id_to_device_map(bridge_device), driver.hue_identifier_to_device_record
to_update,
build_hue_id_to_device_map(bridge_device),
-- These are the hue light/other service ids rather than the hue device ids
utils.get_hue_id_to_device_table_by_bridge(driver, bridge_device) or {}
)
-- check if number of children has changed and if we need to move it
update_index = #devices ~= #group.devices
Expand Down Expand Up @@ -241,7 +248,10 @@ function grouped_utils.group_add(driver, bridge_device, to_add)
end
local hue_light_group =
build_hue_light_group(
to_add, build_hue_id_to_device_map(bridge_device), driver.hue_identifier_to_device_record
to_add,
build_hue_id_to_device_map(bridge_device),
-- These are the hue light/other service ids rather than the hue device ids
utils.get_hue_id_to_device_table_by_bridge(driver, bridge_device) or {}
)
insert(groups, hue_light_group)
log.info(string.format("Adding group %s, %d devices",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function hue_bridge_utils.do_bridge_network_init(driver, bridge_device, bridge_u
{ [HueApi.APPLICATION_KEY_HEADER] = api_key },
nil
)
local hue_identifier_to_device_record = utils.get_hue_id_to_device_table_by_bridge(driver, bridge_device) or {}

eventsource.onopen = function(msg)
log.info_with({ hub_logs = true },
Expand Down Expand Up @@ -171,7 +172,7 @@ function hue_bridge_utils.do_bridge_network_init(driver, bridge_device, bridge_u
local resource_ids = {}
if update_data.type == "zigbee_connectivity" and update_data.owner ~= nil then
for rid, rtype in pairs(driver.services_for_device_rid[update_data.owner.rid] or {}) do
if driver.hue_identifier_to_device_record[rid] then
if hue_identifier_to_device_record[rid] then
log.debug(string.format("Adding RID %s to resource_ids", rid))
table.insert(resource_ids, rid)
end
Expand All @@ -186,7 +187,7 @@ function hue_bridge_utils.do_bridge_network_init(driver, bridge_device, bridge_u
end
for _, resource_id in ipairs(resource_ids) do
log.debug(string.format("Looking for device record for %s", resource_id))
local child_device = driver.hue_identifier_to_device_record[resource_id]
local child_device = hue_identifier_to_device_record[resource_id]
if child_device ~= nil and child_device.id ~= nil then
if update_data.type == "zigbee_connectivity" then
log.debug("emitting event for zigbee connectivity")
Expand All @@ -203,7 +204,7 @@ function hue_bridge_utils.do_bridge_network_init(driver, bridge_device, bridge_u
for _, delete_data in ipairs(event.data) do
if HueDeviceTypes.can_join_device_for_service(delete_data.type) then
local resource_id = delete_data.id
local child_device = driver.hue_identifier_to_device_record[resource_id]
local child_device = hue_identifier_to_device_record[resource_id]
if child_device ~= nil and child_device.id ~= nil then
log.info(
string.format(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local utils = require "utils"

local SensorMultiServiceHelper = {}
function SensorMultiServiceHelper.update_multi_service_device_maps(driver, device, hue_device_id, sensor_info)
local svc_rids_for_device = driver.services_for_device_rid[hue_device_id] or {}
Expand All @@ -15,8 +17,9 @@ function SensorMultiServiceHelper.update_multi_service_device_maps(driver, devic
end

driver.services_for_device_rid[hue_device_id] = svc_rids_for_device
local hue_id_to_device = utils.get_hue_id_to_device_table_by_bridge(driver, device)
for rid, _ in pairs(driver.services_for_device_rid[hue_device_id]) do
driver.hue_identifier_to_device_record[rid] = device
hue_id_to_device[rid] = device
end
end

Expand Down
22 changes: 22 additions & 0 deletions drivers/SmartThings/philips-hue/src/utils/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ end
---@param driver HueDriver
---@param device HueDevice
---@param parent_device_id string?
---@param quiet boolean?
---@return HueBridgeDevice? bridge_device
function utils.get_hue_bridge_for_device(driver, device, parent_device_id, quiet)
local _ = quiet or
Expand All @@ -371,6 +372,27 @@ function utils.get_hue_bridge_for_device(driver, device, parent_device_id, quiet
return utils.get_hue_bridge_for_device(driver, parent_device, nil, quiet)
end

--- Get the mapping of hue id to device table by associated bridge. The mapping is separated by bridge to account
--- for devices migrated to a new hue bridge.
---@param driver HueDriver
---@param bridge_or_device HueDevice
---@return table<string,HueChildDevice>? hue_id_to_device
function utils.get_hue_id_to_device_table_by_bridge(driver, bridge_or_device)
-- If bridge_or_device is a bridge this will just return itself
local bridge = utils.get_hue_bridge_for_device(driver, bridge_or_device)
if not bridge then
log.warn(string.format("Failed to lookup bridge for device %s", bridge_or_device.label))
return nil
end
local bridge_id = bridge:get_field(Fields.BRIDGE_ID)
if not bridge_id then
log.warn(string.format("Failed to get bridge id for %s", bridge.label))
return nil
end
driver.hue_identifier_to_device_record_by_bridge[bridge_id] = driver.hue_identifier_to_device_record_by_bridge[bridge_id] or {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to log if driver.hue_identifier_to_device_record_by_bridge[bridge_id] is nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so since it would be expected the first time that we try to get a table for a certain bridge

return driver.hue_identifier_to_device_record_by_bridge[bridge_id]
end

--- build a exponential backoff time value generator
---
---@param max number the maximum wait interval (not including `rand factor`)
Expand Down
Loading