homebridge-pentair-intellicenter-ai
Version:
Homebridge plugin for the Pentair IntelliCenter pool control system (maintained with AI assistance)
217 lines • 11.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PumpWattsAccessory = void 0;
const types_1 = require("./types");
const settings_1 = require("./settings");
const constants_1 = require("./constants");
const MODEL = 'Pump WATTS Sensor';
/**
* Pump WATTS Accessory
* Displays calculated WATTS (power consumption) for pumps as a light sensor (lux value) for easy visualization in Home app
* Each physical pump gets its own dedicated WATTS sensor based on the pump name and type
*/
class PumpWattsAccessory {
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
this.currentRpm = 0;
this.systemDrivenRpm = 0; // Track heater/system driven speeds separately
this.lastSystemUpdate = 0; // Timestamp of last system update
this.pump = accessory.context.pump;
// Map telnet SubType to actual pump type (SPEED -> VS, VSF -> VSF)
this.pumpType = constants_1.PUMP_TYPE_MAPPING.get(this.pump.type) || 'VS';
// Set accessory information
this.accessory
.getService(this.platform.Service.AccessoryInformation)
.setCharacteristic(this.platform.Characteristic.Manufacturer, settings_1.MANUFACTURER)
.setCharacteristic(this.platform.Characteristic.Model, MODEL)
.setCharacteristic(this.platform.Characteristic.SerialNumber, `WATTS-${this.pump.id}`);
// Get or create the Light Sensor service
this.service =
this.accessory.getService(this.platform.Service.LightSensor) || this.accessory.addService(this.platform.Service.LightSensor);
// Set the service name to match the accessory display name
this.service.setCharacteristic(this.platform.Characteristic.Name, this.accessory.displayName);
// Set initial WATTS value
this.updateWatts(this.getCurrentWatts());
// Configure the current ambient light level characteristic
this.service.getCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel).onGet(this.getWatts.bind(this));
}
/**
* Calculate current WATTS from highest active pump circuit speed or system-driven speed
*/
getCurrentWatts() {
// Find the highest speed from all active pump circuits
const highestActiveRpm = this.getHighestActivePumpSpeed();
// Strategy: Use system-driven speeds (heater) if recent, otherwise use active circuits
let effectiveRpm = highestActiveRpm;
let source = 'active circuits';
// Check if we have a recent system-driven speed update (within last 30 seconds)
const now = Date.now();
const systemUpdateAge = now - this.lastSystemUpdate;
const isSystemUpdateRecent = systemUpdateAge < 30000; // 30 seconds
if (isSystemUpdateRecent && this.systemDrivenRpm > 0) {
effectiveRpm = this.systemDrivenRpm;
source = `system update (${Math.round(systemUpdateAge / 1000)}s ago)`;
this.platform.log.debug(`${this.accessory.displayName}: Using system-driven speed ${this.systemDrivenRpm} RPM ` +
`(age: ${Math.round(systemUpdateAge / 1000)}s, active circuits: ${highestActiveRpm} RPM)`);
}
else if (this.systemDrivenRpm > 0 && !isSystemUpdateRecent) {
// System update is old, fall back to active circuits
this.systemDrivenRpm = 0; // Clear old system speed
this.platform.log.debug(`${this.accessory.displayName}: System update too old (${Math.round(systemUpdateAge / 1000)}s), ` +
`falling back to active circuits: ${highestActiveRpm} RPM`);
}
if (effectiveRpm <= 0) {
return 0.0001; // HomeKit Light Sensor minimum value for inactive pumps
}
const pumpCurve = constants_1.PUMP_PERFORMANCE_CURVES[this.pumpType];
if (!pumpCurve) {
this.platform.log.warn(`Unknown pump type: ${this.pumpType} for pump ${this.pump.name}. Using VS curves.`);
return constants_1.PUMP_PERFORMANCE_CURVES.VS.calculateWATTS(effectiveRpm);
}
const calculatedWatts = pumpCurve.calculateWATTS(effectiveRpm);
this.platform.log.debug(`${this.accessory.displayName}: Calculating WATTS for ${this.pumpType} pump at ${effectiveRpm} RPM ` +
`(source: ${source}) = ${calculatedWatts} WATTS`);
return Math.max(0.0001, calculatedWatts); // Ensure minimum HomeKit value
}
/**
* Find the highest speed from all active pump circuits
*/
getHighestActivePumpSpeed() {
if (!this.pump.circuits || this.pump.circuits.length === 0) {
return 0;
}
let highestSpeed = 0;
const activeSpeeds = [];
// Check all pump circuits and find active ones with their speeds
for (const pumpCircuit of this.pump.circuits) {
// Ensure speed is a number for proper comparison
const speed = Number(pumpCircuit.speed);
if (speed && speed > 0) {
// Try to determine if this circuit is active by checking if we can find
// a corresponding feature or circuit that's ON
const isActive = this.isPumpCircuitActive(pumpCircuit.circuitId);
if (isActive) {
activeSpeeds.push(speed);
if (speed > highestSpeed) {
highestSpeed = speed;
}
}
}
}
this.platform.log.debug(`${this.pump.name}: Active pump speeds: [${activeSpeeds.join(', ')}], highest: ${highestSpeed} RPM`);
return highestSpeed;
}
/**
* Check if a pump circuit is currently active by looking for corresponding feature/circuit status
*/
checkStandardCircuitActive(circuitId) {
var _a;
for (const [, accessory] of this.platform.accessoryMap) {
if (accessory.context.circuit && accessory.context.circuit.id === circuitId) {
const isOn = accessory.context.circuit.status === types_1.CircuitStatus.On;
this.platform.log.debug(`Found circuit ${circuitId}: status = ${accessory.context.circuit.status}, active = ${isOn}`);
return isOn;
}
if (accessory.context.feature && accessory.context.feature.id === circuitId) {
const isOn = accessory.context.feature.status === types_1.CircuitStatus.On;
this.platform.log.debug(`Found feature ${circuitId}: status = ${accessory.context.feature.status}, active = ${isOn}`);
return isOn;
}
if (accessory.context.body && ((_a = accessory.context.body.circuit) === null || _a === void 0 ? void 0 : _a.id) === circuitId) {
const isOn = accessory.context.body.status === types_1.CircuitStatus.On;
this.platform.log.debug(`Found body circuit ${circuitId}: status = ${accessory.context.body.status}, active = ${isOn}`);
return isOn;
}
}
return null; // Not found
}
checkHeaterCircuitActive(circuitId) {
this.platform.log.debug(`Circuit ${circuitId} appears to be internal heater circuit, checking heater status...`);
for (const [, accessory] of this.platform.accessoryMap) {
if (accessory.context.body && accessory.context.body.status === types_1.CircuitStatus.On) {
const body = accessory.context.body;
if (body.heaterId && body.heaterId !== '00000') {
this.platform.log.debug(`[homebridge-pentair-intellicenter-ai] Body ${body.name} ALL FIELDS: ${JSON.stringify(body)}`);
const currentTemp = Number(body.temperature) || 0;
const setPoint = Number(body.lowTemperature) || 0;
const isActivelyHeating = currentTemp < setPoint;
this.platform.log.debug(`[homebridge-pentair-intellicenter-ai] Body ${body.name}: heater ${body.heaterId}, ` +
`temp ${currentTemp}°F, setpoint ${setPoint}°F, actively heating: ${isActivelyHeating} ` +
`[${currentTemp} < ${setPoint} = ${currentTemp < setPoint}]`);
if (isActivelyHeating) {
this.platform.log.debug(`Found actively heating heater for body ${body.name}, circuit ${circuitId} is active`);
return true;
}
}
}
}
this.platform.log.debug(`No actively heating heaters found, circuit ${circuitId} is inactive`);
return false;
}
isPumpCircuitActive(circuitId) {
// Check standard circuits first
const standardResult = this.checkStandardCircuitActive(circuitId);
if (standardResult !== null) {
return standardResult;
}
// Check if this is an internal heater circuit
if (circuitId && circuitId.startsWith('X')) {
return this.checkHeaterCircuitActive(circuitId);
}
// If we can't find the circuit, assume it's inactive
this.platform.log.debug(`Circuit ${circuitId} not found in accessories, assuming inactive`);
return false;
}
/**
* Handle requests to get the current WATTS value
*/
async getWatts() {
const watts = this.getCurrentWatts();
this.platform.log.debug(`Get WATTS for ${this.pump.name}: ${watts}`);
return watts;
}
/**
* Update the current RPM and recalculate WATTS (for circuit-driven updates)
*/
updateSpeed(rpm) {
this.platform.log.debug(`${this.pump.name} updateSpeed called with ${rpm} RPM (previous: ${this.currentRpm} RPM)`);
this.currentRpm = rpm;
const newWatts = this.getCurrentWatts();
this.updateWatts(newWatts);
this.platform.log.debug(`${this.pump.name} speed updated to ${rpm} RPM, calculated WATTS: ${newWatts}`);
}
/**
* Update system-driven RPM (for heater/standalone pump updates)
*/
updateSystemSpeed(rpm) {
this.platform.log.debug(`${this.pump.name} updateSystemSpeed called with ${rpm} RPM (previous system: ${this.systemDrivenRpm} RPM)`);
this.systemDrivenRpm = rpm;
this.lastSystemUpdate = Date.now();
const newWatts = this.getCurrentWatts();
this.updateWatts(newWatts);
this.platform.log.debug(`${this.pump.name} system speed updated to ${rpm} RPM, calculated WATTS: ${newWatts}`);
}
/**
* Update the WATTS display (called when pump speed changes)
*/
updateWatts(watts) {
this.platform.log.debug(`Updating ${this.pump.name} WATTS display to: ${watts}`);
// Update the light sensor value - WATTS maps directly to lux (1:1 ratio)
this.service.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, watts);
}
/**
* Get the current pump type for debugging
*/
getPumpType() {
return this.pumpType;
}
/**
* Get the current RPM for debugging
*/
getCurrentRpm() {
return this.currentRpm;
}
}
exports.PumpWattsAccessory = PumpWattsAccessory;
//# sourceMappingURL=pumpWattsAccessory.js.map