@pietrolubini/homebridge-ecoflow
Version:
Homebridge plugin for EcoFlow devices
143 lines • 6.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EcoFlowAccessoryBase = void 0;
const deviceInfo_1 = require("@ecoflow/apis/containers/deviceInfo");
const characteristicContracts_1 = require("@ecoflow/characteristics/characteristicContracts");
const accessoryInformationService_1 = require("@ecoflow/services/accessoryInformationService");
class EcoFlowAccessoryBase {
platform;
accessory;
config;
log;
httpApiManager;
mqttApiManager;
_services = [];
reconnectMqttTimeoutId = null;
isMqttConnected = false;
subscriptions = [];
deviceInfo;
setReplies = {};
constructor(platform, accessory, config, log, httpApiManager, mqttApiManager) {
this.platform = platform;
this.accessory = accessory;
this.config = config;
this.log = log;
this.httpApiManager = httpApiManager;
this.mqttApiManager = mqttApiManager;
this.deviceInfo = new deviceInfo_1.DeviceInfo(config, log);
}
// Getter for services
get services() {
return this._services;
}
async initialize() {
this._services = this.getServices();
this._services.push(new accessoryInformationService_1.AccessoryInformationService(this));
this.initializeServices();
await this.connectMqtt();
}
cleanupServices() {
const services = this.services.map(service => service.service);
this.accessory.services
.filter(service => !services.includes(service))
.forEach(service => {
this.log.warn('Removing obsolete service from accessory:', service.displayName === undefined || service.displayName === '' ? service.name : service.displayName);
this.accessory.removeService(service);
});
this.services
.filter(service => this.accessory.services.includes(service.service))
.forEach(service => service.cleanupCharacteristics());
}
destroy() {
if (this.reconnectMqttTimeoutId !== null) {
clearTimeout(this.reconnectMqttTimeoutId);
this.reconnectMqttTimeoutId = null;
}
this.subscriptions.forEach(subscription => subscription.unsubscribe());
}
async sendSetCommand(message, revert) {
message.id = Math.floor(Math.random() * 1000000);
message.version = '1.0';
const messageKey = this.getMqttSetMessageKey(message);
const timeoutId = setTimeout(() => {
const command = this.setReplies[messageKey];
if (!command) {
this.log.debug('Timed out message is already processed. Ignore it:', message.id);
}
else {
this.log.warn('Sending of command is timed out. Reverts value back for:', message.id);
revert();
this.setStatus(false);
delete this.setReplies[messageKey];
}
}, this.config.setReplyWaitResponseTimeoutMs ?? 3000);
this.setReplies[messageKey] = { requestMessage: message, revert, timeoutId };
await this.mqttApiManager.sendSetCommand(this.deviceInfo, message);
}
subscribeOnParameterUpdates() {
const subscriptions = [
this.mqttApiManager.subscribeOnQuotaMessage(this.deviceInfo, this.processQuotaMessage.bind(this)),
this.mqttApiManager.subscribeOnSetReplyMessage(this.deviceInfo, this.processSetReplyMessage.bind(this)),
this.mqttApiManager.subscribeOnStatusMessage(this.deviceInfo, this.processStatusMessage.bind(this)),
];
return subscriptions.filter(subscription => !!subscription);
}
processStatusMessage(message) {
const online = message.params.status === characteristicContracts_1.EnableType.On;
this.setStatus(online);
}
processSetReplyMessage(message) {
const messageKey = this.getMqttSetMessageKey(message);
const command = this.setReplies[messageKey];
if (!command) {
this.log.debug('Received "SetReply" response was not sent by accessory. Ignore it:', message);
return;
}
clearTimeout(command.timeoutId);
this.log.debug('Received "SetReply" response:', message);
delete this.setReplies[messageKey];
// Detect whether response is successfull for different contracts (e.g. data.ack, data.result, data.configOk)
if ((message.data.ack === undefined && message.data.result === undefined && message.data.configOk === undefined) ||
(message.data.ack !== undefined && Boolean(message.data.ack) !== false) ||
(message.data.result !== undefined && message.data.result !== false) ||
(message.data.configOk !== undefined && message.data.configOk === false)) {
this.log.warn('Failed to set a value. Reverts value back for:', command.requestMessage.id);
command.revert();
}
else {
this.log.debug('Setting of a value was successful for:', command.requestMessage.id);
}
}
getMqttSetMessageKey(message) {
return message.id.toString();
}
initializeServices() {
this.services.forEach(service => {
service.initialize();
});
}
async connectMqtt() {
await this.initMqtt();
this.reconnectMqttTimeoutId = setInterval(async () => {
// Check MQTT is connected every minute
if (!this.isMqttConnected) {
await this.initMqtt();
}
}, this.config.reconnectMqttTimeoutMs ?? 60000);
}
async initMqtt() {
this.isMqttConnected =
(await this.mqttApiManager.subscribeOnQuotaTopic(this.deviceInfo)) &&
(await this.mqttApiManager.subscribeOnSetReplyTopic(this.deviceInfo)) &&
(await this.mqttApiManager.subscribeOnStatusTopic(this.deviceInfo));
if (this.isMqttConnected) {
this.subscriptions = this.subscribeOnParameterUpdates();
}
}
setStatus(online) {
this.log.warn(`Device is ${online ? 'online' : 'offline'}`);
this.services.forEach(service => service.updateReachability(online));
}
}
exports.EcoFlowAccessoryBase = EcoFlowAccessoryBase;
//# sourceMappingURL=ecoFlowAccessoryBase.js.map