Files
myprimal/src/mqtt/commands.js
T
arnaudne 941abd98d5 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>
2026-06-28 01:05:24 +02:00

57 lines
1.8 KiB
JavaScript

'use strict';
const { getClient, getPrefix, publish } = require('./client');
const { allOutputs } = require('../registry');
const { sendCommand } = require('../control');
function subscribeAll() {
const client = getClient();
if (!client) return;
const prefix = getPrefix();
for (const out of allOutputs()) {
const topic = `${prefix}/${out.moduleSlug}/output/${out.index}/set`;
client.subscribe(topic);
}
client.on('message', async (topic, buf) => {
const prefix_ = `${prefix}/`;
if (!topic.startsWith(prefix_)) return;
// Match: {prefix}/{slug}/output/{index}/set
const parts = topic.slice(prefix_.length).split('/');
if (parts.length !== 4 || parts[1] !== 'output' || parts[3] !== 'set') return;
const [slug, , indexStr] = parts;
const outputIndex = parseInt(indexStr, 10);
let cmd;
try {
cmd = JSON.parse(buf.toString());
} catch {
console.warn(`[commands] Invalid JSON on ${topic}`);
return;
}
// Find module by slug
const output = allOutputs().find(o => o.moduleSlug === slug && o.index === outputIndex);
if (!output) {
console.warn(`[commands] No output found for ${slug}/output/${outputIndex}`);
return;
}
try {
await sendCommand(output.moduleId, outputIndex, cmd);
// Reflect running state optimistically
const stateValue = (cmd.action === 'run' || cmd.action === 'boost') ? 1 : 0;
publish(`${slug}/output/${outputIndex}/state`, { value: stateValue, ts: new Date().toISOString() });
console.log(`[commands] ${slug}/output/${outputIndex}${cmd.action}`);
} catch (e) {
console.error(`[commands] Command failed for ${slug}/output/${outputIndex}:`, e.message);
}
});
console.log(`[commands] Subscribed to ${allOutputs().length} output set topics`);
}
module.exports = { subscribeAll };