feat: initial myprimal MQTT bridge service
Dynamic MySolem/MyIndygo IoT bridge with MQTT, Home Assistant autodiscovery, and Homebridge EasyMQTT compatibility. - Device discovery via MySolem API on startup (all modules/inputs/outputs) - Configurable poll engine (fast/normal/slow buckets, per-input overrides) - MQTT retained state publishing + HA autodiscovery payloads - Unified manual command routing (linesControl vs sendManualModuleCommand) - Runtime config via MQTT $config/set, persisted to config.json - Expression override system (API-provided > hardcoded > passthrough) - Minimal REST API: /health, /state, /control, /rediscover - Docker deployment (Dockerfile + docker-compose.yml) - 38 unit tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
const { getClient, getPrefix } = require('./client');
|
||||
const { allInputs, allOutputs } = require('../registry');
|
||||
const { getInputType } = require('../input-types');
|
||||
|
||||
function publishAll() {
|
||||
const client = getClient();
|
||||
if (!client) return;
|
||||
const prefix = getPrefix();
|
||||
|
||||
for (const inp of allInputs()) {
|
||||
const typeDef = getInputType(inp.typeCode);
|
||||
const stateTopic = `${prefix}/${inp.moduleSlug}/sensor/${inp.metric}`;
|
||||
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,
|
||||
device: {
|
||||
identifiers: [`myprimal_${inp.moduleId}`],
|
||||
name: inp.moduleName,
|
||||
manufacturer: 'Solem',
|
||||
},
|
||||
};
|
||||
|
||||
if (typeDef.deviceClass) payload.device_class = typeDef.deviceClass;
|
||||
if (typeDef.unit) payload.unit_of_measurement = typeDef.unit;
|
||||
|
||||
const component = typeDef.haComponent;
|
||||
const configTopic = `homeassistant/${component}/myprimal_${inp.id}/config`;
|
||||
client.publish(configTopic, JSON.stringify(payload), { retain: true });
|
||||
}
|
||||
|
||||
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 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,
|
||||
device: {
|
||||
identifiers: [`myprimal_${out.moduleId}`],
|
||||
name: out.moduleName,
|
||||
manufacturer: 'Solem',
|
||||
},
|
||||
};
|
||||
|
||||
const configTopic = `homeassistant/switch/myprimal_${out.moduleId}_${out.index}/config`;
|
||||
client.publish(configTopic, JSON.stringify(payload), { retain: true });
|
||||
}
|
||||
|
||||
// Publish availability online for all modules
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[discovery] HA autodiscovery published');
|
||||
}
|
||||
|
||||
module.exports = { publishAll };
|
||||
Reference in New Issue
Block a user