UNPKG

homebridge-sense-energy-monitor

Version:

Enhanced Homebridge plugin for Sense Home Energy Monitor with comprehensive API integration and real-time monitoring

102 lines 5 kB
import { getEveCharacteristics } from './eveCharacteristics.js'; import { PLUGIN_VERSION } from './settings.js'; const PERSIST_INTERVAL_MS = 5 * 60 * 1000; const MAX_ENERGY_GAP_HOURS = 1; const DEFAULT_VOLTAGE = 120; export class EnergyMonitorAccessory { platform; accessory; outletService; eve; historyService = null; lastEnergyAt = 0; lastPersistAt = Date.now(); constructor(platform, accessory) { this.platform = platform; this.accessory = accessory; const { Service: HapService, Characteristic } = platform.api.hap; this.eve = getEveCharacteristics(platform.api); const information = accessory.getService(HapService.AccessoryInformation) ?? accessory.addService(HapService.AccessoryInformation); information .setCharacteristic(Characteristic.Manufacturer, 'Sense Labs') .setCharacteristic(Characteristic.Model, 'Energy Monitor') .setCharacteristic(Characteristic.SerialNumber, String(platform.senseApi?.monitorId ?? 'Unknown')) .setCharacteristic(Characteristic.FirmwareRevision, PLUGIN_VERSION); // Reuse the cached Outlet service — removing and re-adding it every // boot is what used to break HomeKit automations. this.outletService = accessory.getService(HapService.Outlet) ?? accessory.addService(HapService.Outlet, accessory.displayName); this.outletService.getCharacteristic(Characteristic.On) .onGet(() => this.isOn()) .onSet(() => { // The outlet is a read-only power indicator; snap back to reality. setTimeout(() => { this.outletService.updateCharacteristic(Characteristic.On, this.isOn()); }, 100); }); this.outletService.getCharacteristic(Characteristic.OutletInUse) .onGet(() => this.isOn()); for (const characteristic of [this.eve.Consumption, this.eve.Voltage, this.eve.ElectricCurrent, this.eve.TotalConsumption]) { if (!this.outletService.testCharacteristic(characteristic)) { this.outletService.addCharacteristic(new characteristic()); } } const context = this.context(); context.type = 'main'; context.totalConsumptionKwh ??= 0; this.attachHistory(); } context() { return this.accessory.context; } isOn() { const power = this.platform.senseApi?.realtime.power ?? 0; return power > (this.platform.pluginConfig?.devicePowerThreshold ?? 10); } attachHistory() { const FakeGato = this.platform.fakeGatoService; if (!this.platform.pluginConfig?.enableHistory || !FakeGato) { return; } try { this.historyService = new FakeGato('energy', this.accessory, { storage: 'fs', path: this.platform.api.user.storagePath(), }); } catch (error) { this.platform.log.warn(`Failed to create Eve history service: ${error.message}`); } } update(data) { const { Characteristic } = this.platform.api.hap; const isOn = data.power > (this.platform.pluginConfig?.devicePowerThreshold ?? 10); const voltage = data.voltage.length > 0 ? data.voltage.reduce((sum, v) => sum + v, 0) / data.voltage.length : DEFAULT_VOLTAGE; const current = voltage > 0 ? data.power / voltage : 0; // Integrate power into the lifetime kWh accumulator. Eve expects a // monotonic total, so this lives in accessory.context and survives // restarts via the accessory cache. const now = Date.now(); const context = this.context(); if (this.lastEnergyAt > 0 && now > this.lastEnergyAt) { const hours = Math.min((now - this.lastEnergyAt) / 3_600_000, MAX_ENERGY_GAP_HOURS); context.totalConsumptionKwh = (context.totalConsumptionKwh ?? 0) + (data.power * hours) / 1000; } this.lastEnergyAt = now; this.outletService.updateCharacteristic(Characteristic.On, isOn); this.outletService.updateCharacteristic(Characteristic.OutletInUse, isOn); this.outletService.updateCharacteristic(this.eve.Consumption, data.power); this.outletService.updateCharacteristic(this.eve.Voltage, Math.round(voltage * 10) / 10); this.outletService.updateCharacteristic(this.eve.ElectricCurrent, Math.round(current * 100) / 100); this.outletService.updateCharacteristic(this.eve.TotalConsumption, Math.round((context.totalConsumptionKwh ?? 0) * 1000) / 1000); this.historyService?.addEntry({ time: Math.round(now / 1000), power: data.power }); if (now - this.lastPersistAt >= PERSIST_INTERVAL_MS) { this.lastPersistAt = now; this.platform.api.updatePlatformAccessories([this.accessory]); } } } //# sourceMappingURL=energyMonitorAccessory.js.map