diff --git a/src/mqtt/discovery.js b/src/mqtt/discovery.js index e7d2d30..f8cef64 100644 --- a/src/mqtt/discovery.js +++ b/src/mqtt/discovery.js @@ -3,6 +3,12 @@ const { getClient, getPrefix } = require('./client'); const { allInputs, allOutputs } = require('../registry'); const { getInputType } = require('../input-types'); +// Availability payload is JSON so EasyMQTT (wildcard subscriber) can parse it. +// HA uses availability_template to extract the state field. +const AVAIL_ONLINE = JSON.stringify({ state: 'online' }); +const AVAIL_OFFLINE = JSON.stringify({ state: 'offline' }); +const AVAIL_TEMPLATE = '{{ value_json.state }}'; + function publishAll() { const client = getClient(); if (!client) return; @@ -14,11 +20,14 @@ function publishAll() { const availTopic = `${prefix}/${inp.moduleSlug}/availability`; const payload = { - name: inp.name || inp.metric, - unique_id: `myprimal_${inp.id}`, - state_topic: stateTopic, - value_template: '{{ value_json.value }}', - availability_topic: availTopic, + name: inp.name || inp.metric, + unique_id: `myprimal_${inp.id}`, + state_topic: stateTopic, + value_template: '{{ value_json.value }}', + availability_topic: availTopic, + availability_template: AVAIL_TEMPLATE, + payload_available: 'online', + payload_not_available: 'offline', device: { identifiers: [`myprimal_${inp.moduleId}`], name: inp.moduleName, @@ -35,21 +44,24 @@ function publishAll() { } for (const out of allOutputs()) { - const stateTopic = `${prefix}/${out.moduleSlug}/output/${out.index}/state`; - const cmdTopic = `${prefix}/${out.moduleSlug}/output/${out.index}/set`; - const availTopic = `${prefix}/${out.moduleSlug}/availability`; + const stateTopic = `${prefix}/${out.moduleSlug}/output/${out.index}/state`; + const cmdTopic = `${prefix}/${out.moduleSlug}/output/${out.index}/set`; + const availTopic = `${prefix}/${out.moduleSlug}/availability`; const payload = { - name: out.name, - unique_id: `myprimal_${out.moduleId}_${out.index}`, - state_topic: stateTopic, - command_topic: cmdTopic, - payload_on: JSON.stringify({ action: 'run', duration: 30 }), - payload_off: JSON.stringify({ action: 'stop' }), - value_template: '{{ value_json.value }}', - state_on: '1', - state_off: '0', - availability_topic: availTopic, + name: out.name, + unique_id: `myprimal_${out.moduleId}_${out.index}`, + state_topic: stateTopic, + command_topic: cmdTopic, + payload_on: JSON.stringify({ action: 'run', duration: 30 }), + payload_off: JSON.stringify({ action: 'stop' }), + value_template: '{{ value_json.value }}', + state_on: '1', + state_off: '0', + availability_topic: availTopic, + availability_template: AVAIL_TEMPLATE, + payload_available: 'online', + payload_not_available: 'offline', device: { identifiers: [`myprimal_${out.moduleId}`], name: out.moduleName, @@ -61,16 +73,28 @@ function publishAll() { client.publish(configTopic, JSON.stringify(payload), { retain: true }); } - // Publish availability online for all modules + // Publish availability for all modules (JSON payload) const seen = new Set(); for (const inp of allInputs()) { if (!seen.has(inp.moduleSlug)) { seen.add(inp.moduleSlug); - getClient().publish(`${prefix}/${inp.moduleSlug}/availability`, 'online', { retain: true }); + client.publish(`${prefix}/${inp.moduleSlug}/availability`, AVAIL_ONLINE, { retain: true }); + } + } + for (const out of allOutputs()) { + if (!seen.has(out.moduleSlug)) { + seen.add(out.moduleSlug); + client.publish(`${prefix}/${out.moduleSlug}/availability`, AVAIL_ONLINE, { retain: true }); } } console.log('[discovery] HA autodiscovery published'); } -module.exports = { publishAll }; +function publishOffline(moduleSlug) { + const client = getClient(); + if (!client) return; + client.publish(`${getPrefix()}/${moduleSlug}/availability`, AVAIL_OFFLINE, { retain: true }); +} + +module.exports = { publishAll, publishOffline };