From eccc34450c05fd204281e6d86216c537d89b225b Mon Sep 17 00:00:00 2001 From: Robert Masen Date: Thu, 8 May 2025 14:24:14 -0500 Subject: [PATCH] add memory-per-device driver --- .../collectGarbage-presentation.yaml | 16 ++++ .../capabilities/collectGarbage.yaml | 10 +++ memory_per_device/config.yml | 5 ++ memory_per_device/profiles/mpd-profile.yml | 6 ++ .../profiles/mpd_bridge-profile.yml | 8 ++ memory_per_device/readme.md | 1 + memory_per_device/src/disco.lua | 90 +++++++++++++++++++ memory_per_device/src/driver_name.lua | 1 + memory_per_device/src/init.lua | 66 ++++++++++++++ 9 files changed, 203 insertions(+) create mode 100644 memory_per_device/capabilities/collectGarbage-presentation.yaml create mode 100644 memory_per_device/capabilities/collectGarbage.yaml create mode 100644 memory_per_device/config.yml create mode 100644 memory_per_device/profiles/mpd-profile.yml create mode 100644 memory_per_device/profiles/mpd_bridge-profile.yml create mode 100644 memory_per_device/readme.md create mode 100644 memory_per_device/src/disco.lua create mode 100644 memory_per_device/src/driver_name.lua create mode 100644 memory_per_device/src/init.lua diff --git a/memory_per_device/capabilities/collectGarbage-presentation.yaml b/memory_per_device/capabilities/collectGarbage-presentation.yaml new file mode 100644 index 0000000..513fec7 --- /dev/null +++ b/memory_per_device/capabilities/collectGarbage-presentation.yaml @@ -0,0 +1,16 @@ +id: honestadmin11679.collectgarbage +version: 1 +dashboard: + states: + - label: Collect Garbage + alternatives: [] + basicPlus: [] + actions: + - displayType: pushButton + pushButton: + command: collect +detailView: + - label: Create Target + displayType: pushButton + pushButton: + command: collect diff --git a/memory_per_device/capabilities/collectGarbage.yaml b/memory_per_device/capabilities/collectGarbage.yaml new file mode 100644 index 0000000..99e307f --- /dev/null +++ b/memory_per_device/capabilities/collectGarbage.yaml @@ -0,0 +1,10 @@ +id: honestadmin11679.collectgarbage +version: 1 +status: proposed +name: collectgarbage +ephemeral: false +attributes: {} +commands: + collect: + name: collect + arguments: [] diff --git a/memory_per_device/config.yml b/memory_per_device/config.yml new file mode 100644 index 0000000..8f8b8ed --- /dev/null +++ b/memory_per_device/config.yml @@ -0,0 +1,5 @@ +name: 'memory_per_device' +packageKey: 'memory_per_device' +permissions: + lan: {} + discovery: {} diff --git a/memory_per_device/profiles/mpd-profile.yml b/memory_per_device/profiles/mpd-profile.yml new file mode 100644 index 0000000..13ddffa --- /dev/null +++ b/memory_per_device/profiles/mpd-profile.yml @@ -0,0 +1,6 @@ +name: mpd_sensor.v1 +components: +- id: main + capabilities: + - id: switch + version: 1 diff --git a/memory_per_device/profiles/mpd_bridge-profile.yml b/memory_per_device/profiles/mpd_bridge-profile.yml new file mode 100644 index 0000000..b5d5b56 --- /dev/null +++ b/memory_per_device/profiles/mpd_bridge-profile.yml @@ -0,0 +1,8 @@ +name: mpd_bridge.v1 +components: + - id: main + capabilities: + - id: honestadmin11679.targetcreate + version: 1 + - id: honestadmin11679.collectgarbage + version: 1 diff --git a/memory_per_device/readme.md b/memory_per_device/readme.md new file mode 100644 index 0000000..9eae785 --- /dev/null +++ b/memory_per_device/readme.md @@ -0,0 +1 @@ +# Memory Per Device diff --git a/memory_per_device/src/disco.lua b/memory_per_device/src/disco.lua new file mode 100644 index 0000000..f8fb528 --- /dev/null +++ b/memory_per_device/src/disco.lua @@ -0,0 +1,90 @@ +local json = require 'dkjson' +local log = require 'log' +local utils = require 'st.utils' + +--- Add a new device to this driver +--- +---@param driver Driver The driver instance to use +---@param device_number number|nil If populated this will be used to generate the device name/label if not, `get_device_list` +--- will be called to provide this value +local function add_sensor_device(driver, device_number, parent_device_id) + log.trace('add_sensor_device') + if device_number == nil then + log.debug('determining current device count') + local device_list = driver.device_api.get_device_list() + device_number = #device_list + end + local device_name = 'MPD ' .. device_number + log.debug('adding device ' .. device_name) + local device_id = utils.generate_uuid_v4() .. tostring(device_number) .. '2' + local device_info = { + type = 'LAN', + deviceNetworkId = device_id, + label = device_name, + parent_device_id = parent_device_id, + profileReference = 'mpd_sensor.v1', + vendorProvidedName = device_name, + } + local device_info_json = json.encode(device_info) + local success, msg = driver.device_api.create_device(device_info_json) + if success then + log.debug('successfully created device') + return device_name, device_id + end + log.error(string.format('unsuccessful create_device (sensor) %s', msg)) +end + +local function add_bridge_device(driver) + log.trace('add_bridge_device') + local device_id = utils.generate_uuid_v4() + local device_name = "MPD Bridge" + local device_info = { + type = 'LAN', + deviceNetworkId = device_id, + label = device_name, + profileReference = 'mpd_bridge.v1', + vendorProvidedName = device_name, + } + local device_info_json = json.encode(device_info) + local success, msg = driver.device_api.create_device(device_info_json) + if success then + log.debug('successfully created device') + return device_name, device_id + end + log.error(string.format('unsuccessful create_device (bridge) %s', msg)) + return nil, nil, msg +end + +--- A discovery pass that will discover exactly 1 device +--- for a driver. I any devices are already associated with +--- this driver, no devices will be discovered +--- +---@param driver Driver the driver name to use when discovering a device +---@param opts table the discovery options +---@param cont function function to check if discovery should continue +local function disco_handler(driver, opts, cont) + log.trace('disco') + + if cont() then + local device_list = driver:get_devices() + log.trace('starting discovery') + for _idx,device in ipairs(device_list or {}) do + if device:supports_capability_by_id("honestadmin11679.targetcreate") then + return + end + end + local device_name, device_id, err = add_bridge_device(driver) + if err ~= nil then + log.error(err) + return + end + log.info('added new device ' .. device_name) + end +end + + + +return { + disco_handler = disco_handler, + add_sensor_device = add_sensor_device, +} diff --git a/memory_per_device/src/driver_name.lua b/memory_per_device/src/driver_name.lua new file mode 100644 index 0000000..6b74ee9 --- /dev/null +++ b/memory_per_device/src/driver_name.lua @@ -0,0 +1 @@ +return "memory_per_device" diff --git a/memory_per_device/src/init.lua b/memory_per_device/src/init.lua new file mode 100644 index 0000000..1ccba45 --- /dev/null +++ b/memory_per_device/src/init.lua @@ -0,0 +1,66 @@ +local Driver = require 'st.driver' +local capabilities = require 'st.capabilities' +local discovery = require 'disco' + +local createTargetId = "honestadmin11679.targetcreate" +local createTarget = capabilities[createTargetId]; +local collectGarbageId = "honestadmin11679.collectgarbage" +local collectGarbage = capabilities[collectGarbageId]; + +local function is_bridge(device) + return device:supports_capability_by_id(collectGarbageId) +end + +local function emit_switch(driver, device, on) + local ev + if on then + ev = capabilities.switch.switch.on() + else + ev = capabilities.switch.switch.off() + end + device:emit_event(ev) +end + +local function device_init(driver, device) + if not is_bridge(device) then + emit_switch(driver, device, false) + end +end + +local driver = Driver(require("driver_name"), { + lifecycle_handlers = { + init = device_init, + added = device_init, + }, + discovery = discovery.disco_handler, + capability_handlers = { + [createTargetId] = { + create = function(driver, device) + discovery.add_sensor_device(driver, nil, driver.bridge_id) + end + }, + [collectGarbageId] = { + collect = function(driver, device) + collectgarbage("collect") + collectgarbage("collect") + memory.trim() + end, + create = function(driver, device) + collectgarbage("collect") + collectgarbage("collect") + memory.trim() + end, + }, + [capabilities.switch.ID] = { + [capabilities.switch.commands.on.NAME] = function(driver, device) + emit_switch(driver, device, true) + end, + [capabilities.switch.commands.off.NAME] = function(driver, device) + emit_switch(driver, device, false) + end, + }, + } +}) + + +driver:run()