From 83883ce1a94f5178aebb54179c0f65e8bfb9706f Mon Sep 17 00:00:00 2001 From: Sam Stenvall Date: Thu, 15 Aug 2024 10:13:51 +0300 Subject: [PATCH] Expose "acknowledge alarm" functionality over MQTT and HTTP Closes #114 A button is exposed to Home Assistant and a POST /alarm/acknowledge route is exposed via HTTP --- app/homeassistant.mjs | 20 ++++++++++++++++++++ app/http.mjs | 14 ++++++++++++++ app/modbus.mjs | 4 ++++ app/mqtt.mjs | 17 ++++++++++++++--- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/app/homeassistant.mjs b/app/homeassistant.mjs index 89a36a3..c304e04 100644 --- a/app/homeassistant.mjs +++ b/app/homeassistant.mjs @@ -337,12 +337,22 @@ export const configureMqttDiscovery = async (modbusClient, mqttClient) => { 'defrosting': createDeviceStateConfiguration(configurationBase, 'defrosting', 'Defrosting'), } + // Button for acknowledging alarms + const buttonConfigurationMap = { + 'acknowledgeAlarm': createButtonConfiguration( + configurationBase, + 'acknowledgeAlarm', + 'Acknowledge newest alarm' + ), + } + // Final map that describes everything we want to be auto-discovered const configurationMap = { 'sensor': sensorConfigurationMap, 'number': numberConfigurationMap, 'switch': switchConfigurationMap, 'binary_sensor': binarySensorConfigurationMap, + 'button': buttonConfigurationMap, } // Publish configurations @@ -476,3 +486,13 @@ const createDeviceStateConfiguration = (configurationBase, stateName, entityName 'entity_category': 'diagnostic', } } + +const createButtonConfiguration = (configurationBase, buttonName, entityName) => { + return { + ...configurationBase, + 'unique_id': `eda-button-${buttonName}`, + 'name': entityName, + 'object_id': `eda_button_${buttonName}`, + 'command_topic': `${TOPIC_PREFIX_ALARM}/acknowledge`, + } +} diff --git a/app/http.mjs b/app/http.mjs index d032d3b..7373fab 100644 --- a/app/http.mjs +++ b/app/http.mjs @@ -6,6 +6,7 @@ import { getSettings, setMode as modbusSetMode, setSetting as modbusSetSetting, + acknowledgeAlarm as modbusAcknowledgeAlarm, getDeviceState, getNewestAlarm, getAlarmSummary, @@ -92,6 +93,16 @@ const setSetting = async (modbusClient, req, res) => { } } +const acknowledgeAlarm = async (modbusClient, req, res) => { + try { + logger.info('Acknowledging currently active alarm (if any)') + + await modbusAcknowledgeAlarm(modbusClient) + } catch (e) { + handleError(e, res) + } +} + export const configureRoutes = (httpServer, modbusClient) => { httpServer.get('/', root) httpServer.get('/summary', (req, res) => { @@ -106,6 +117,9 @@ export const configureRoutes = (httpServer, modbusClient) => { httpServer.post('/setting/:setting/:value', (req, res) => { return setSetting(modbusClient, req, res) }) + httpServer.post('/alarm/acknowledge', (req, res) => { + return acknowledgeAlarm(modbusClient, req, res) + }) } const handleError = (e, res, statusCode = undefined) => { diff --git a/app/modbus.mjs b/app/modbus.mjs index 450be74..65baee5 100644 --- a/app/modbus.mjs +++ b/app/modbus.mjs @@ -374,6 +374,10 @@ export const getNewestAlarm = async (modbusClient) => { } } +export const acknowledgeAlarm = async (modbusClient) => { + await tryWriteHoldingRegister(modbusClient, 386, 1) +} + export const getDeviceState = async (modbusClient) => { const result = await mutex.runExclusive(async () => tryReadHoldingRegisters(modbusClient, 44, 1)) diff --git a/app/mqtt.mjs b/app/mqtt.mjs index 88150d1..a0e7f4f 100644 --- a/app/mqtt.mjs +++ b/app/mqtt.mjs @@ -7,6 +7,7 @@ import { setMode, getAlarmSummary, getDeviceState, + acknowledgeAlarm, } from './modbus.mjs' import { createLogger } from './logger.mjs' @@ -121,8 +122,12 @@ const publishTopics = async (mqttClient, topicMap, publishOptions = {}) => { } export const subscribeToChanges = async (modbusClient, mqttClient) => { - // Subscribe to settings and mode changes - const topicNames = [`${TOPIC_PREFIX_MODE}/+/set`, `${TOPIC_PREFIX_SETTINGS}/+/set`] + // Subscribe to writable topics + const topicNames = [ + `${TOPIC_PREFIX_MODE}/+/set`, + `${TOPIC_PREFIX_SETTINGS}/+/set`, + `${TOPIC_PREFIX_ALARM}/acknowledge`, + ] for (const topicName of topicNames) { logger.info(`Subscribing to topic(s) ${topicName}`) @@ -136,8 +141,8 @@ export const handleMessage = async (modbusClient, mqttClient, topicName, rawPayl const payload = parsePayload(rawPayload) - // Handle settings updates if (topicName.startsWith(TOPIC_PREFIX_SETTINGS) && topicName.endsWith('/set')) { + // Handle settings updates const settingName = topicName.substring(TOPIC_PREFIX_SETTINGS.length + 1, topicName.lastIndexOf('/')) logger.info(`Updating setting ${settingName} to ${payload}`) @@ -145,12 +150,18 @@ export const handleMessage = async (modbusClient, mqttClient, topicName, rawPayl await setSetting(modbusClient, settingName, payload) await publishSettings(modbusClient, mqttClient) } else if (topicName.startsWith(TOPIC_PREFIX_MODE) && topicName.endsWith('/set')) { + // Handle mode changes const mode = topicName.substring(TOPIC_PREFIX_MODE.length + 1, topicName.lastIndexOf('/')) logger.info(`Updating mode ${mode} to ${payload}`) await setMode(modbusClient, mode, payload) await publishModeSummary(modbusClient, mqttClient) + } else if (topicName.startsWith(TOPIC_PREFIX_ALARM) && topicName.endsWith('/acknowledge')) { + // Acknowledge alarm + logger.info('Acknowledging currently active alarm (if any)') + + await acknowledgeAlarm(modbusClient) } }