@elshaer/homebridge-hdl-buspro-enhanced
Version:
Linking the HDL bus into the Homebridge widget
183 lines • 8.55 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RelayHeater = void 0;
class RelayHeater {
constructor(platform, accessory, name, controller, device,
// Listener is currently unused but needs to stay in the signature to align with the shared builder
// that passes the relay listener instance.
listener, channel, minTemperature, maxTemperature, defaultTemperature) {
this.platform = platform;
this.accessory = accessory;
this.name = name;
this.controller = controller;
this.device = device;
this.listener = listener;
this.channel = channel;
this.minTemperature = minTemperature;
this.maxTemperature = maxTemperature;
this.defaultTemperature = defaultTemperature;
this.heaterState = {
active: 0, // 0 = Off, 1 = On
currentTemp: 20,
targetTemp: 22,
};
// HDL MFH06.432 Specific Commands
this.HDL_HEATER_CONTROL_CMD = 0xE3E0;
this.HDL_TEMP_SET_CMD = 0xE3E1;
this.HDL_STATUS_QUERY_CMD = 0xE3E3;
void this.listener;
const Service = this.platform.Service;
const Characteristic = this.platform.Characteristic;
// Accessory Information
this.accessory.getService(Service.AccessoryInformation)
.setCharacteristic(Characteristic.Manufacturer, 'HDL')
.setCharacteristic(Characteristic.Model, 'MFH06.432')
.setCharacteristic(Characteristic.SerialNumber, `HDL-HEATER-${this.channel}`);
// Thermostat Service
this.service = this.accessory.getService(Service.Thermostat) ||
this.accessory.addService(Service.Thermostat);
this.service.setCharacteristic(Characteristic.Name, name);
if (this.defaultTemperature !== undefined) {
this.heaterState.targetTemp = this.clampTargetTemperature(this.defaultTemperature);
}
// Setup Characteristics
this.configureCharacteristics();
this.setupEventListeners();
this.initializeHeater();
}
configureCharacteristics() {
const Characteristic = this.platform.Characteristic;
const { minTarget, maxTarget } = this.getTargetBounds();
// Active State
this.service.getCharacteristic(Characteristic.Active)
.onGet(() => this.heaterState.active)
.onSet((value) => this.setPowerState(value));
// Temperature Characteristics
this.service.getCharacteristic(Characteristic.CurrentTemperature)
.setProps({
minValue: Math.min(0, minTarget),
maxValue: Math.max(40, maxTarget),
minStep: 0.5,
})
.onGet(() => this.heaterState.currentTemp);
this.service.getCharacteristic(Characteristic.TargetTemperature)
.setProps({ minValue: minTarget, maxValue: maxTarget, minStep: 0.5 })
.onGet(() => this.heaterState.targetTemp)
.onSet((value) => this.setTargetTemperature(value));
// Heating State Characteristics
this.service.getCharacteristic(Characteristic.CurrentHeatingCoolingState)
.onGet(() => this.heaterState.active ? 1 : 0);
this.service.getCharacteristic(Characteristic.TargetHeatingCoolingState)
.setProps({ validValues: [0, 1] })
.onGet(() => this.heaterState.active ? 1 : 0)
.onSet((value) => this.setPowerState(value));
}
setupEventListeners() {
// Listen for status updates (MFH06.432 uses 0xE3E4 for broadcast)
this.device.on(0xE3E4, (command) => {
if (command.data.channel === this.channel) {
this.updateFromStatus(command.data);
}
});
}
initializeHeater() {
// Query initial state
this.controller.send({
target: this.device,
command: this.HDL_STATUS_QUERY_CMD,
data: { channel: this.channel },
}, (err, res) => {
if (err) {
this.platform.log.error(`Failed to query heater status for ${this.name}: ${err.message ?? err}`);
return;
}
if (res?.data) {
this.updateFromStatus(res.data);
return;
}
this.platform.log.warn(`Heater ${this.name} did not return status data on query.`);
});
}
updateFromStatus(data) {
const Characteristic = this.platform.Characteristic;
// Update power state
if (data.power !== undefined) {
this.heaterState.active = data.power ? 1 : 0;
this.service.updateCharacteristic(Characteristic.Active, this.heaterState.active);
this.service.updateCharacteristic(Characteristic.CurrentHeatingCoolingState, this.heaterState.active ? Characteristic.CurrentHeatingCoolingState.HEAT :
Characteristic.CurrentHeatingCoolingState.OFF);
this.service.updateCharacteristic(Characteristic.TargetHeatingCoolingState, this.heaterState.active ? Characteristic.TargetHeatingCoolingState.HEAT :
Characteristic.TargetHeatingCoolingState.OFF);
}
// Update temperatures (direct values, no scaling needed)
if (data.currentTemp !== undefined) {
this.heaterState.currentTemp = data.currentTemp;
this.service.updateCharacteristic(Characteristic.CurrentTemperature, this.heaterState.currentTemp);
}
if (data.targetTemp !== undefined) {
this.heaterState.targetTemp = this.clampTargetTemperature(data.targetTemp);
this.service.updateCharacteristic(Characteristic.TargetTemperature, this.heaterState.targetTemp);
}
}
setPowerState(state) {
const Characteristic = this.platform.Characteristic;
const targetState = state ? 1 : 0;
this.controller.send({
target: this.device,
command: this.HDL_HEATER_CONTROL_CMD,
data: {
channel: this.channel,
status: targetState,
},
}, (err) => {
if (err) {
this.platform.log.error(`Failed to set heater power for ${this.name}: ${err.message ?? err}`);
this.service.updateCharacteristic(Characteristic.Active, this.heaterState.active);
return;
}
this.heaterState.active = targetState;
this.service.updateCharacteristic(Characteristic.Active, targetState);
this.service.updateCharacteristic(Characteristic.TargetHeatingCoolingState, targetState ? Characteristic.TargetHeatingCoolingState.HEAT :
Characteristic.TargetHeatingCoolingState.OFF);
this.service.updateCharacteristic(Characteristic.CurrentHeatingCoolingState, targetState ? Characteristic.CurrentHeatingCoolingState.HEAT :
Characteristic.CurrentHeatingCoolingState.OFF);
this.platform.log.info(`Power state set to ${targetState ? 'ON' : 'OFF'}`);
});
}
setTargetTemperature(temp) {
const Characteristic = this.platform.Characteristic;
const previousTemp = this.heaterState.targetTemp;
const targetTemp = this.clampTargetTemperature(temp);
this.controller.send({
target: this.device,
command: this.HDL_TEMP_SET_CMD,
data: {
channel: this.channel,
temperature: targetTemp,
},
}, (err) => {
if (err) {
this.platform.log.error(`Failed to set target temperature for ${this.name}: ${err.message ?? err}`);
this.service.updateCharacteristic(Characteristic.TargetTemperature, previousTemp);
return;
}
this.heaterState.targetTemp = targetTemp;
this.service.updateCharacteristic(Characteristic.TargetTemperature, targetTemp);
this.platform.log.info(`Target temperature set to ${this.heaterState.targetTemp}°C`);
});
}
getTargetBounds() {
const minTarget = this.minTemperature ?? 15;
const maxTarget = this.maxTemperature ?? 35;
return {
minTarget,
maxTarget: Math.max(minTarget, maxTarget),
};
}
clampTargetTemperature(temp) {
const { minTarget, maxTarget } = this.getTargetBounds();
return Math.min(maxTarget, Math.max(minTarget, temp));
}
}
exports.RelayHeater = RelayHeater;
//# sourceMappingURL=RelayHeater.js.map