import { PowerState } from "../db/models/index.js"; function numOrNull(v) { if (v == null || v === "") return null; const n = Number(v); return Number.isFinite(n) ? n : null; } export async function getOrCreatePowerState() { let doc = await PowerState.findOne(); if (!doc) { doc = await PowerState.create({}); } return doc; } /** * Client + history sample shape aligned with PowerState.schema nested fields. * Merges legacy flat keys still present on some documents (`batteryPercent`, `chargingVoltage`). * * @param {import("mongoose").Document | Record} power */ export function formatPowerStateForClient(power) { const o = power?.toObject ? power.toObject() : { ...(power || {}) }; const bat = o.battery && typeof o.battery === "object" ? o.battery : {}; const sol = o.solar && typeof o.solar === "object" ? o.solar : {}; const load = o.load && typeof o.load === "object" ? o.load : {}; return { battery: { voltage: numOrNull(bat.voltage ?? o.chargingVoltage), current: numOrNull(bat.current), percent: numOrNull(bat.percent ?? o.batteryPercent), }, solar: { voltage: numOrNull(sol.voltage), current: numOrNull(sol.current), }, load: { voltage: numOrNull(load.voltage), current: numOrNull(load.current), }, updatedAt: o.updatedAt ?? null, }; } /** @returns {Promise} whether power doc was updated */ export async function applyPowerFromPayload(payload) { if (!payload || typeof payload !== "object") return false; const bat = payload.battery; const hasNestedBat = bat && typeof bat === "object" && (bat.percent != null || bat.voltage != null || bat.current != null); const hasFlat = payload.batteryPercent != null || payload.chargingVoltage != null || payload.chargingCurrent != null || payload.current != null; const sol = payload.solar; const hasSolar = sol && typeof sol === "object" && (sol.voltage != null || sol.current != null); const ld = payload.load; const hasLoad = ld && typeof ld === "object" && (ld.voltage != null || ld.current != null); if (!hasNestedBat && !hasFlat && !hasSolar && !hasLoad) { return false; } const pow = await getOrCreatePowerState(); if (hasFlat) { if (!pow.battery) pow.battery = {}; if (payload.batteryPercent != null) { pow.battery.percent = Number(payload.batteryPercent); } if (payload.chargingVoltage != null) { pow.battery.voltage = Number(payload.chargingVoltage); } if (payload.chargingCurrent != null || payload.current != null) { pow.battery.current = Number(payload.chargingCurrent ?? payload.current); } } if (hasNestedBat) { if (!pow.battery) pow.battery = {}; if (bat.percent != null) pow.battery.percent = Number(bat.percent); if (bat.voltage != null) pow.battery.voltage = Number(bat.voltage); if (bat.current != null) pow.battery.current = Number(bat.current); } if (hasSolar) { if (!pow.solar) pow.solar = {}; if (sol.voltage != null) pow.solar.voltage = Number(sol.voltage); if (sol.current != null) pow.solar.current = Number(sol.current); } if (hasLoad) { if (!pow.load) pow.load = {}; if (ld.voltage != null) pow.load.voltage = Number(ld.voltage); if (ld.current != null) pow.load.current = Number(ld.current); } await pow.save(); return true; }