const fs = require("fs"); const path = require("path"); async function createPlugin(ctx) { return { async execute(action) { if (action === "enableTx") { return setTxState(ctx, true); } if (action === "disableTx") { return setTxState(ctx, false); } if (action === "getTxState") { return getTxState(ctx); } throw new Error(`Unknown action: ${action}`); }, async getStatus() { return getTxState(ctx); }, async health() { return { ok: true }; } }; } async function setTxState(ctx, nextActive) { const stateFilePath = resolvePath(String(ctx.getSetting("stateFilePath", defaultStateFilePath(ctx)))); const timeoutMs = Number(ctx.getSetting("timeoutMs", ctx.env.TX_CONTROL_TIMEOUT_MS || 20000)); const settingValue = String(ctx.getSetting(nextActive ? "enableCommand" : "disableCommand", "")).trim(); const envValue = String(nextActive ? (ctx.env.TX_ENABLE_CMD || defaultEnableCommand()) : (ctx.env.TX_DISABLE_CMD || defaultDisableCommand())).trim(); const command = settingValue || envValue; const simulate = typeof ctx.simulateHardware === "boolean" ? ctx.simulateHardware : !(process.platform === "linux" || String(ctx.env.ALLOW_NON_LINUX_CMDS || "false") === "true"); if (!command && !simulate) { throw new Error(nextActive ? "TX enable command missing" : "TX disable command missing"); } const normalizedCommand = String(command || "").trim().toLowerCase(); const commandIsNoopSuccess = normalizedCommand === "true" || normalizedCommand === ":"; if (command && !simulate && !commandIsNoopSuccess) { const result = await ctx.commandRunner(command, { timeoutMs }); if (!result.ok) { throw new Error(result.stderr || result.error || `TX ${nextActive ? "enable" : "disable"} failed`); } } await writeStateFile(stateFilePath, { txActive: nextActive, updatedAt: new Date().toISOString(), source: "rms.tx.control.native" }); return { ok: true, txActive: nextActive, updatedAt: new Date().toISOString(), source: "rms.tx.control.native", skipped: (simulate && Boolean(command)) || commandIsNoopSuccess, message: simulate ? `TX ${nextActive ? "ON" : "OFF"} simuliert (${ctx.execMode || "dev"})` : `TX ${nextActive ? "ON" : "OFF"}` }; } async function getTxState(ctx) { const statusCommand = String(ctx.getSetting("statusCommand", ctx.env.TX_STATUS_CMD || defaultStatusCommand())).trim(); const stateFilePath = resolvePath(String(ctx.getSetting("stateFilePath", defaultStateFilePath(ctx)))); const timeoutMs = Number(ctx.getSetting("timeoutMs", ctx.env.TX_CONTROL_TIMEOUT_MS || 20000)); const simulate = typeof ctx.simulateHardware === "boolean" ? ctx.simulateHardware : !(process.platform === "linux" || String(ctx.env.ALLOW_NON_LINUX_CMDS || "false") === "true"); if (statusCommand && !simulate) { const result = await ctx.commandRunner(statusCommand, { timeoutMs }); if (result.ok) { const text = String(result.stdout || "").trim().toLowerCase(); if (text === "1" || text === "true" || text === "on") { return { txActive: true, source: "rms.tx.control.native", updatedAt: new Date().toISOString() }; } if (text === "0" || text === "false" || text === "off") { return { txActive: false, source: "rms.tx.control.native", updatedAt: new Date().toISOString() }; } } } const fallback = { txActive: false, source: "rms.tx.control.native", updatedAt: null }; if (!stateFilePath || !fs.existsSync(stateFilePath)) { return fallback; } try { const raw = fs.readFileSync(stateFilePath, "utf8"); const parsed = JSON.parse(raw); return { txActive: Boolean(parsed && parsed.txActive), source: parsed && parsed.source ? parsed.source : "rms.tx.control.native", updatedAt: parsed && parsed.updatedAt ? parsed.updatedAt : null, details: parsed && typeof parsed === "object" ? parsed : null }; } catch { return fallback; } } async function writeStateFile(filePath, state) { if (!filePath) return; await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); await fs.promises.writeFile(filePath, JSON.stringify(state, null, 2)); } function defaultStateFilePath(ctx) { const explicit = String(ctx.env.TX_STATE_PATH || "").trim(); if (explicit) { return explicit; } const dataDir = String(ctx.env.DATA_DIR || "").trim(); if (dataDir) { return path.join(dataDir, "tx-state.json"); } return "./data/tx-state.json"; } function defaultEnableCommand() { return "/opt/remotestation/bin/tx-control.sh enable"; } function defaultDisableCommand() { return "/opt/remotestation/bin/tx-control.sh disable"; } function defaultStatusCommand() { return "/opt/remotestation/bin/tx-control.sh status"; } function resolvePath(value) { const v = String(value || "").trim(); if (!v) return ""; if (path.isAbsolute(v)) return v; return path.resolve(process.cwd(), v); } module.exports = { createPlugin };