const express = require("express"); const deviceManager = require("./deviceManager"); const thingsboard = require("./thingsboard"); const config = require("./config"); const router = express.Router(); function asyncHandler(fn) { return (req, res, next) => { try { Promise.resolve(fn(req, res, next)).catch(next); } catch (error) { next(error); } }; } function idsFromBody(body) { const ids = body?.ids; if (!Array.isArray(ids) || ids.length === 0) { const error = new Error("ids[] is required"); error.status = 400; throw error; } return ids.map(String); } router.get("/health", asyncHandler(async (_req, res) => { const tb = await thingsboard.ping(); res.json({ ok: true, mqttUrl: config.tbMqttUrl, thingsboard: tb, }); })); router.get("/telemetry-schema", (_req, res) => { res.json(deviceManager.telemetrySchema()); }); router.get("/device-profiles", asyncHandler(async (_req, res) => { const profiles = await thingsboard.listDeviceProfiles(); const preferred = profiles.find((profile) => profile.preferred); res.json({ profiles, defaultProfileId: preferred?.id || config.tbDeviceProfileId || null, }); })); router.get("/devices", (_req, res) => { res.json({ devices: deviceManager.list() }); }); router.post("/devices", asyncHandler(async (req, res) => { const device = await deviceManager.create({ name: req.body?.name, label: req.body?.label, type: req.body?.type, deviceProfileId: req.body?.deviceProfileId, accessToken: req.body?.accessToken, id: req.body?.id, }); res.status(201).json({ device }); })); router.post("/devices/adopt", asyncHandler(async (req, res) => { const device = await deviceManager.adopt({ name: req.body?.name, label: req.body?.label, type: req.body?.type, }); res.status(201).json({ device }); })); router.delete("/devices/:id", asyncHandler(async (req, res) => { const result = await deviceManager.remove(req.params.id); res.json(result); })); router.post("/devices/delete", asyncHandler(async (req, res) => { const results = await deviceManager.removeMany(idsFromBody(req.body)); res.json({ results }); })); router.patch("/devices/:id/published-keys", asyncHandler(async (req, res) => { const device = deviceManager.setPublishedKeys(req.params.id, req.body?.keys); res.json({ device }); })); router.patch("/devices/:id/telemetry", asyncHandler(async (req, res) => { const device = deviceManager.updateTelemetry(req.params.id, req.body || {}); res.json({ device }); })); router.post("/devices/:id/telemetry/start", asyncHandler(async (req, res) => { res.json({ device: deviceManager.startTelemetry(req.params.id) }); })); router.post("/devices/:id/telemetry/stop", asyncHandler(async (req, res) => { res.json({ device: deviceManager.stopTelemetry(req.params.id) }); })); router.post("/devices/:id/telemetry/publish", asyncHandler(async (req, res) => { const device = deviceManager.updateTelemetry(req.params.id, req.body || {}); res.json({ device }); })); router.post("/devices/:id/interval", asyncHandler(async (req, res) => { res.json({ device: deviceManager.setIntervalMs(req.params.id, req.body?.intervalMs) }); })); router.patch("/devices/:id/plant", asyncHandler(async (req, res) => { res.json({ device: deviceManager.setPlant(req.params.id, req.body || {}) }); })); router.post("/devices/:id/disable", asyncHandler(async (req, res) => { res.json({ device: deviceManager.disable(req.params.id) }); })); router.post("/devices/:id/enable", asyncHandler(async (req, res) => { res.json({ device: deviceManager.enable(req.params.id) }); })); router.post("/devices/disable", asyncHandler(async (req, res) => { res.json({ results: deviceManager.applyMany(idsFromBody(req.body), deviceManager.disable) }); })); router.post("/devices/enable", asyncHandler(async (req, res) => { res.json({ results: deviceManager.applyMany(idsFromBody(req.body), deviceManager.enable) }); })); router.use((_req, res) => { res.status(404).json({ error: "Not found" }); }); router.use((error, _req, res, _next) => { const status = error.status || 500; res.status(status).json({ error: error.message || "Unexpected error", timedOut: Boolean(error.timedOut), }); }); module.exports = router;