Skip to content

Aqara-FP2: Add error handling and modify offline logic #2235

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
30 changes: 23 additions & 7 deletions drivers/Aqara/aqara-presence-sensor/src/discovery.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
local log = require "log"
local discovery = {}
local fields = require "fields"
local discovery_mdns = require "discovery_mdns"
local socket = require "cosock.socket"

local processing_devices = {}
local discovery = {
last_try_time = {}
}

function discovery.set_device_field(driver, device)
local device_cache_value = driver.datastore.discovery_cache[device.device_network_id]
Expand Down Expand Up @@ -36,6 +37,10 @@ local function try_add_device(driver, device_dni, device_ip)

update_device_discovery_cache(driver, device_dni, device_ip, device_info)
local create_device_msg = driver.discovery_helper.get_device_create_msg(driver, device_dni, device_ip, device_info)
if not create_device_msg then
log.error_with({ hub_logs = true }, string.format("Failed to get device info. dni= %s, ip= %s", device_dni, device_ip))
return "device info not found"
end

local credential = driver.discovery_helper.get_credential(driver, device_dni, device_ip)

Expand All @@ -52,15 +57,20 @@ local function try_add_device(driver, device_dni, device_ip)
end

log.info_with({ hub_logs = true }, string.format("try_create_device. dni= %s, ip= %s", device_dni, device_ip))
processing_devices[device_dni] = true
driver:try_create_device(create_device_msg)

local success, ret = pcall(driver.try_create_device, driver, create_device_msg)
if success then
discovery.last_try_time[device_dni] = os.time()
else
log.error_with({ hub_logs = true }, string.format("Failed to try_create_device. dni= %s, %s", device_dni, ret))
end
return nil
end

function discovery.device_added(driver, device)
log.info_with({ hub_logs = true }, string.format("device_added. dni= %s", device.device_network_id))
discovery.set_device_field(driver, device)
processing_devices[device.device_network_id] = nil
discovery.last_try_time[device.device_network_id] = nil
driver.lifecycle_handlers.init(driver, device)
end

Expand Down Expand Up @@ -100,14 +110,20 @@ local function discovery_device(driver)
break
end
end
if (not processing_devices[dni]) and (not is_already_added) then
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like this will cause a lot of extra device added messages from the driver to the cloud if we have already started adding the device to the platform, but the hub hasn't gotten the device record from the platform yet. Why is the processing_devices not needed anymore? It seems like its purpose is to prevent sending multiply try_create_device requests, which seems like something that we want to do.

try_add_device(driver, dni, ip)
if not is_already_added then
local time_since_last_try = os.time() - (discovery.last_try_time[dni] or 0)
if (not discovery.last_try_time[dni]) or (time_since_last_try > 10) then
try_add_device(driver, dni, ip)
else
log.trace(string.format("skip adding device because it was tried recently. dni= %s, ip= %s, since %s sec", dni, ip, time_since_last_try))
end
end
end
end

function discovery.do_network_discovery(driver, _, should_continue)
log.info_with({ hub_logs = true }, string.format("discovery start for Aqara FP2"))
discovery.last_try_time = {}
while should_continue() do
discovery_device(driver)
socket.sleep(1)
Expand Down
2 changes: 1 addition & 1 deletion drivers/Aqara/aqara-presence-sensor/src/fields.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ local fields = {
MONITORING_TIMER = "monitoring_timer",
CREDENTIAL = "credential",
_INIT = "init",
CONNECTION_STATUS = "connection_status"
LAST_DISCONNECTED_TIME = "last_disconnected_time"
}

return fields
25 changes: 15 additions & 10 deletions drivers/Aqara/aqara-presence-sensor/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ local multipleZonePresence = require "multipleZonePresence"
local EventSource = require "lunchbox.sse.eventsource"

local DEFAULT_MONITORING_INTERVAL = 300
local OFFLINE_TIMEOUT = 150
local CREDENTIAL_KEY_HEADER = "Authorization"

local function handle_sse_event(driver, device, msg)
Expand Down Expand Up @@ -54,23 +55,27 @@ local function create_sse(driver, device, credential)
end
end


eventsource.onerror = function()
local DISCONNECTED_STATUS = "disconnected"
local connection_status = device:get_field(fields.CONNECTION_STATUS)
if connection_status and connection_status == DISCONNECTED_STATUS then
log.error(string.format("Eventsource error: dni= %s", device.device_network_id))
local last_disconnected_time = device:get_field(fields.LAST_DISCONNECTED_TIME)
local now = os.time()

if not last_disconnected_time then
log.error_with({ hub_logs = true }, string.format("Eventsource error: disconnected, dni= %s, time = %s, host=%s", device.device_network_id, now, eventsource.url.host))
device:set_field(fields.LAST_DISCONNECTED_TIME, now)
else
log.error_with({ hub_logs = true }, string.format("Eventsource error: disconnected, dni= %s", device.device_network_id))
device:set_field(fields.CONNECTION_STATUS, DISCONNECTED_STATUS)
local time_diff = os.difftime(now, last_disconnected_time)
if time_diff > OFFLINE_TIMEOUT then
log.error_with({ hub_logs = true }, string.format("Eventsource error: Offline. disconnected for %s secs, dni= %s", time_diff, device.device_network_id))
device:offline()
end
end
device:offline()

end

eventsource.onopen = function()
log.info_with({ hub_logs = true }, string.format("Eventsource open: dni= %s", device.device_network_id))
log.info_with({ hub_logs = true }, string.format("Eventsource open: dni= %s, host=%s", device.device_network_id, eventsource.url.host))
device:online()
device:set_field(fields.CONNECTION_STATUS, "connected")
device:set_field(fields.LAST_DISCONNECTED_TIME, nil)
local success, err = status_update(driver, device)
if not success then
log.warn(string.format("Failed to status_update during eventsource.onopen, err = %s dni= %s", err, device.device_network_id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ local function closed_action(source)

local sleep_time_secs = source._reconnect_time_millis / 1000.0
socket.sleep(sleep_time_secs)

if source._reconnect_time_millis <= MAX_RECONNECT_TIME_SEC * 1000 then
source._reconnect_time_millis = source._reconnect_time_millis + 1000.0
end
Expand Down Expand Up @@ -506,7 +507,7 @@ function EventSource.new(url, extra_headers, sock_builder)
local _, action_err, partial = state_actions[source.ready_state](source)
if action_err ~= nil then
if action_err ~= "timeout" or action_err ~= "wantread" then
log.error_with({ hub_logs = true }, "Event Source Coroutine State Machine error: " .. action_err)
log.error_with({ hub_logs = true }, "Event Source Coroutine State Machine error(" .. (source.url.host or "") .. "):" .. action_err)
if partial ~= nil and #partial > 0 then
log.error_with({ hub_logs = true }, st_utils.stringify_table(partial, "\tReceived Partial", true))
end
Expand Down