Skip to content

Commit

Permalink
[Aqara] Presence Sensor FP2 (#1546)
Browse files Browse the repository at this point in the history
* add Aqara FP2

* add search-parameters.yml

* Modify "device:supports_capability" parameter

* fix luacheck fail

* Fixed minor issues

* fix zone name initialization

* Change find_new_connetion to find_new_connection

* Fixed zone name initialization issue

* fix luacheck fail

* Modified some code based on comments
  • Loading branch information
seojune79 authored Sep 13, 2024
1 parent b5808a0 commit d2f9765
Show file tree
Hide file tree
Showing 19 changed files with 2,293 additions and 0 deletions.
5 changes: 5 additions & 0 deletions drivers/Aqara/aqara-presence-sensor/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: 'Aqara Presence Sensor'
packageKey: 'aqara-presence-sensor'
permissions:
lan: {}
discovery: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: aqara-fp2-fallDetection
components:
- id: main
capabilities:
- id: activitySensor
version: 1
- id: presenceSensor
version: 1
- id: illuminanceMeasurement
version: 1
- id: refresh
version: 1
categories:
- name: PresenceSensor
- id: mode
capabilities:
- id: stse.deviceMode
version: 1
categories:
- name: PresenceSensor
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: aqara-fp2-sleepMonitoring
components:
- id: main
capabilities:
- id: illuminanceMeasurement
version: 1
- id: refresh
version: 1
categories:
- name: PresenceSensor
- id: mode
capabilities:
- id: stse.deviceMode
version: 1
categories:
- name: PresenceSensor
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: aqara-fp2-zoneDetection
components:
- id: main
capabilities:
- id: presenceSensor
version: 1
- id: movementSensor
version: 1
- id: multipleZonePresence
version: 1
- id: illuminanceMeasurement
version: 1
- id: refresh
version: 1
categories:
- name: PresenceSensor
- id: mode
capabilities:
- id: stse.deviceMode
version: 1
categories:
- name: PresenceSensor
2 changes: 2 additions & 0 deletions drivers/Aqara/aqara-presence-sensor/search-parameters.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mdns:
- service: "_Aqara-FP2._tcp"
93 changes: 93 additions & 0 deletions drivers/Aqara/aqara-presence-sensor/src/discovery.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
local log = require "log"
local discovery = {}
local fields = require "fields"
local discovery_mdns = require "discovery_mdns"
local socket = require "cosock.socket"

-- mapping from device DNI to info needed at discovery/init time
local device_discovery_cache = {}

local function set_device_field(driver, device)
local device_cache_value = device_discovery_cache[device.device_network_id]

-- persistent fields
if device_cache_value ~= nil then
device:set_field(fields.DEVICE_IPV4, device_cache_value.ip, { persist = true })
device:set_field(fields.DEVICE_INFO, device_cache_value.device_info, { persist = true })
device:set_field(fields.CREDENTIAL, device_cache_value.credential, { persist = true })
end
end

local function update_device_discovery_cache(driver, dni, ip, credential)
local device_info = driver.discovery_helper.get_device_info(driver, dni, ip)
device_discovery_cache[dni] = {
ip = ip,
device_info = device_info,
credential = credential,
}
end

local function try_add_device(driver, device_dni, device_ip)
log.trace(string.format("try_add_device : dni= %s, ip= %s", device_dni, device_ip))

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

if not credential then
log.error(string.format("failed to get credential. dni= %s, ip= %s", device_dni, device_ip))
return
end

update_device_discovery_cache(driver, device_dni, device_ip, credential)
local create_device_msg = driver.discovery_helper.get_device_create_msg(driver, device_dni, device_ip)
driver:try_create_device(create_device_msg)
end

function discovery.device_added(driver, device)
set_device_field(driver, device)
device_discovery_cache[device.device_network_id] = nil
driver.lifecycle_handlers.init(driver, device)
end

function discovery.find_ip_table(driver)
return discovery_mdns.find_ip_table_by_mdns(driver)
end

local function discovery_device(driver)
local unknown_discovered_devices = {}
local known_discovered_devices = {}
local known_devices = {}

for _, device in pairs(driver:get_devices()) do
known_devices[device.device_network_id] = device
end

local ip_table = discovery.find_ip_table(driver)

for dni, ip in pairs(ip_table) do
if not known_devices[dni] then
unknown_discovered_devices[dni] = ip
else
known_discovered_devices[dni] = ip
end
end

for dni, ip in pairs(known_discovered_devices) do
log.trace(string.format("known dni= %s, ip= %s", dni, ip))
end

for dni, ip in pairs(unknown_discovered_devices) do
log.trace(string.format("unknown dni= %s, ip= %s", dni, ip))
if not device_discovery_cache[dni] then
try_add_device(driver, dni, ip)
end
end
end

function discovery.do_network_discovery(driver, _, should_continue)
while should_continue() do
discovery_device(driver)
socket.sleep(0.2)
end
end

return discovery
143 changes: 143 additions & 0 deletions drivers/Aqara/aqara-presence-sensor/src/discovery_mdns.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
local log = require "log"
local mdns = require "st.mdns"
local net_utils = require "st.net_utils"

local discovery_mdns = {}

local function byte_array_to_plain_text(byte_array)
return string.char(table.unpack(byte_array))
end

local function get_text_by_srvname(srvname, discovery_responses)
for _, answer_item in pairs(discovery_responses.answers or {}) do
if answer_item.kind.TxtRecord ~= nil and answer_item.name == srvname then
return answer_item.kind.TxtRecord.text
end
end
end

local function get_srvname_by_hostname(hostname, discovery_responses)
for _, answer_item in pairs(discovery_responses.answers or {}) do
if answer_item.kind.SrvRecord ~= nil and answer_item.kind.SrvRecord.target == hostname then
return answer_item.name
end
end
end

local function get_hostname_by_ip(ip, discovery_responses)
for _, answer_item in pairs(discovery_responses.answers or {}) do
if answer_item.kind.ARecord ~= nil and answer_item.kind.ARecord.ipv4 == ip then
return answer_item.name
end
end
end


local function find_text_in_answers_by_ip(ip, discovery_responses)
local hostname = get_hostname_by_ip(ip, discovery_responses)
local srvname = get_srvname_by_hostname(hostname, discovery_responses)
local text = get_text_by_srvname(srvname, discovery_responses)

return text
end

function discovery_mdns.find_text_list_in_mdns_response(driver, ip, discovery_responses)
local text_list = {}

for _, found_item in pairs(discovery_responses.found or {}) do
if found_item.host_info.address == ip then
for _, raw_text_array in pairs(found_item.txt.text or {}) do
local text_item = byte_array_to_plain_text(raw_text_array)
table.insert(text_list, text_item)
end
end
end

local answer_text = find_text_in_answers_by_ip(ip, discovery_responses)
for _, text_item in pairs(answer_text or {}) do
table.insert(text_list, text_item)
end
return text_list
end

local function filter_response_by_service_name(service_type, domain, discovery_responses)
local filtered_responses = {
answers = {},
found = {}
}

for _, answer in pairs(discovery_responses.answers or {}) do
table.insert(filtered_responses.answers, answer)
end

for _, additional in pairs(discovery_responses.additional or {}) do
table.insert(filtered_responses.answers, additional)
end

for _, found in pairs(discovery_responses.found or {}) do
if found.service_info.service_type == service_type then
table.insert(filtered_responses.found, found)
end
end

return filtered_responses
end

local function insert_dni_ip_from_answers(driver, filtered_responses, target_table)
for _, answer in pairs(filtered_responses.answers) do
local dni, ip
log.info("answer_name, arecod = " .. tostring(answer.name) .. ", " .. tostring(answer.kind.ARecord))

if answer.kind.ARecord ~= nil then
ip = answer.kind.ARecord.ipv4
end

if ip ~= nil then
dni = driver.discovery_helper.get_dni(driver, ip, filtered_responses)

if dni ~= nil then
target_table[dni] = ip
end
end
end
end

local function insert_dni_ip_from_found(driver, filtered_responses, target_table)
for _, found in pairs(filtered_responses.found) do
local dni, ip
log.info("found_name = " .. tostring(found.service_info.service_type))
if found.host_info.address ~= nil and net_utils.validate_ipv4_string(found.host_info.address) then
log.info("ip = " .. tostring(found.host_info.address))
ip = found.host_info.address
end

if ip ~= nil then
dni = driver.discovery_helper.get_dni(driver, ip, filtered_responses)

if dni ~= nil then
target_table[dni] = ip
end
end
end
end

local function get_dni_ip_table_from_mdns_responses(driver, service_type, domain, discovery_responses)
local dni_ip_table = {}

local filtered_responses = filter_response_by_service_name(service_type, domain, discovery_responses)

insert_dni_ip_from_answers(driver, filtered_responses, dni_ip_table)
insert_dni_ip_from_found(driver, filtered_responses, dni_ip_table)

return dni_ip_table
end

function discovery_mdns.find_ip_table_by_mdns(driver)
log.info("discovery_mdns.find_device_ips")
local service_type, domain = driver.discovery_helper.get_service_type_and_domain()
local discovery_responses = mdns.discover(service_type, domain) or { found = {} }
local dni_ip_table = get_dni_ip_table_from_mdns_responses(driver, service_type, domain, discovery_responses)
return dni_ip_table
end

return discovery_mdns
16 changes: 16 additions & 0 deletions drivers/Aqara/aqara-presence-sensor/src/fields.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--- Table of constants used to index in to device store fields
--- @module "fields"
--- @class table
--- @field IPV4 string the ipV4 address of the device

local fields = {
DEVICE_IPV4 = "device_ipv4",
DEVICE_INFO = "device_info",
CONN_INFO = "conn_info",
EVENT_SOURCE = "eventsource",
MONITORING_TIMER = "monitoring_timer",
CREDENTIAL = "credential",
_INIT = "init"
}

return fields
Loading

0 comments on commit d2f9765

Please sign in to comment.