@petro-kushchak/homebridge-ch-gree-ac-eve-platform
Version:
Cooper&Hunter (based on Gree AC API) AC plugin for Homebridge (Homekit) with Web Hook/MQTT Data Sharing
270 lines • 12.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Platform = void 0;
const mqtt_1 = __importDefault(require("mqtt"));
const fs = __importStar(require("fs"));
const gree_ac_api_1 = require("gree-ac-api");
const settings_1 = require("./settings");
const PlatformAC_1 = require("./PlatformAC");
const HttpService_1 = require("./services/HttpService");
const configModel_1 = require("./configModel");
const logger_1 = require("./logger");
class Platform {
get devices() {
return this.config.devices;
}
constructor(log, config, api) {
this.log = log;
this.config = config;
this.api = api;
this.Service = this.api.hap.Service;
this.Characteristic = this.api.hap.Characteristic;
this.accessories = [];
this.registeredDevices = [];
// Validate configuration
if ((0, configModel_1.isPluginConfiguration)(config, this.log)) {
this.config = config;
}
else {
this.log.error(`INVALID CONFIGURATION FOR PLUGIN: ${settings_1.PLUGIN_NAME}\nThis plugin will NOT WORK until this problem is resolved.`);
return;
}
gree_ac_api_1.DeviceFinder.on('device-updated', this.onDeviceUpdated.bind(this));
gree_ac_api_1.DeviceFinder.on('device-found', this.onDeviceFound.bind(this));
this.log.debug('Finished initializing platform:', this.config.name);
this.api.on('didFinishLaunching', () => {
this.log.debug('Executed didFinishLaunching callback');
gree_ac_api_1.DeviceFinder.scan(this.config.broadcastAddress, 0);
});
if (this.config.httpPort > 0) {
this.httpService = new HttpService_1.HttpService(this.config.httpPort, this.log);
this.httpService.start((uri) => this.httpHandler(uri));
}
if (this.config.mqtt) {
this.mqttClient = this.initializeMqttClient(this.config);
}
}
httpHandler(uri) {
this.log.info(`Received request: ${uri}`);
const parts = uri.split('/');
if (parts.length < 3) {
return {
error: true,
message: 'Malformed uri',
};
}
// update accessory temp value
// uri example: /temp/<ac-id>/22.5%C2%B0C
// usually due to HomeKit automation when original uri is /temp/123/22.5C
if (parts[1] === 'temp') {
const deviceId = parts[2];
const device = this.registeredDevices.find((plat) => {
this.log.info(`registeredDevices: ${plat.Mac} `);
return plat.Mac === deviceId;
});
this.log.info(`URL parts: device: ${deviceId} temp: ${parts[3]}`);
if (!device) {
this.log.info(`Device mac: ${deviceId} not found`);
return {
error: false,
message: `Device mac: ${deviceId} not found`,
};
}
const tempParts = parts[3].split('%');
if (tempParts.length > 0) {
//replace with "." in case if HomeKit automation sends "," in temperature value
const temp = '' + tempParts[0].replace(',', '.');
device === null || device === void 0 ? void 0 : device.updateProp('currentTemp', temp);
const message = `Updated accessory ${deviceId} current temperature to: ${temp}`;
this.log.info(message);
return {
error: false,
message: message,
};
}
}
return {
error: false,
message: 'OK',
};
}
configureAccessory(accessory) {
this.log.info('Loading accessory from cache:', accessory.displayName);
this.accessories.push(accessory);
}
onDeviceFound(device) {
if (!device.Name) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pack = device['pack'];
pack.cid = device.FullInfo.mac;
pack.name = device.FullInfo.mac;
this.log.info('Updating accessory pack:', JSON.stringify(pack));
device.updatePack(device.FullInfo.ip, device.FullInfo.port, pack);
}
const uuid = this.api.hap.uuid.generate(device.FullInfo.id);
const existingAccessory = this.accessories.find((accessory) => accessory.UUID === uuid);
if (!existingAccessory) {
const deviceName = device.Name ? device.Name : device.FullInfo.mac;
this.log.info('Adding new accessory:', deviceName);
const accessory = new this.api.platformAccessory(deviceName, uuid);
accessory.context = {
data: device.FullInfo,
device: device
};
this.registeredDevices.push(new PlatformAC_1.PlatformAC(this, accessory, this.log));
return this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [
accessory
]);
}
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
existingAccessory.context = {
data: device.FullInfo,
device: device
};
this.registeredDevices.push(new PlatformAC_1.PlatformAC(this, existingAccessory, this.log));
}
onDeviceUpdated(oldDeviceInfo, newDevice) {
const uuid = this.api.hap.uuid.generate(oldDeviceInfo.id);
const device = this.registeredDevices.find((plat) => plat.UUID === uuid);
if (device) {
this.api.updatePlatformAccessories([device.updateDevice(newDevice)]);
}
}
initializeMqttClient(config) {
if (!config.mqtt.server || !config.mqtt.base_topic) {
this.log.error('No MQTT server and/or base_topic defined!');
}
this.log.info(`Connecting to MQTT server at ${config.mqtt.server}`);
const options = Platform.createMqttOptions(this.log, config);
const mqttClient = mqtt_1.default.connect(config.mqtt.server, options);
mqttClient.on('connect', this.onMqttConnected.bind(this));
mqttClient.on('close', this.onMqttClose.bind(this));
this.api.on('didFinishLaunching', () => {
var _a, _b;
if (this.config !== undefined) {
// Setup MQTT callbacks and subscription
(_a = this.mqttClient) === null || _a === void 0 ? void 0 : _a.on('message', this.onMessage.bind(this));
(_b = this.mqttClient) === null || _b === void 0 ? void 0 : _b.subscribe(this.config.mqtt.base_topic + '/#');
}
});
return mqttClient;
}
onMqttConnected() {
this.log.info('Connected to MQTT server');
}
onMqttClose() {
this.log.error('Disconnected from MQTT server!');
}
onMessage(topic, payload) {
var _a;
const fullTopic = topic;
try {
const baseTopic = `${(_a = this.config) === null || _a === void 0 ? void 0 : _a.mqtt.base_topic}/`;
if (!topic.startsWith(baseTopic)) {
this.log.debug('Ignore message, because topic is unexpected.', topic);
return;
}
const deviceConfig = this.devices.find(device => device.sensorTopic === fullTopic);
if (!deviceConfig) {
this.log.debug('Ignore message, because topic is not in the list of devices.', fullTopic);
return;
}
let device;
this.registeredDevices.forEach(plat => {
this.log.info(`registeredDevices: ${plat.Mac} `);
if (plat.Mac === deviceConfig.id) {
device = plat;
}
});
if (!device) {
this.log.debug('Ignore message, because device is not registered.', deviceConfig.id);
return;
}
const info = JSON.parse(payload.toString());
this.log.info(`Received MQTT message '${payload.toString()}' on topic: ${fullTopic} for device: ${deviceConfig.id}`);
if (!(0, configModel_1.isMqttSensor)(info, deviceConfig.sensorTemperatureKey || 'temperature')) {
this.log.error('Ignore message, because payload is not recognised as sensor data.', payload.toString());
return;
}
const sensor = info;
const temperature = !deviceConfig.sensorTemperatureKey ? sensor.temperature : '' + sensor[deviceConfig.sensorTemperatureKey];
device.updateProp('currentTemp', '' + temperature);
this.log.info(`Updated device: ${deviceConfig.id} with temperature: ${temperature}`);
}
catch (err) {
this.log.error(`Failed to process MQTT message on '${fullTopic}'. (Maybe check the MQTT version?)`);
this.log.error((0, logger_1.errorToString)(err));
}
}
static createMqttOptions(log, config) {
const options = {};
if (config.mqtt.version) {
options.protocolVersion = config.mqtt.version;
}
if (config.mqtt.keepalive) {
log.debug(`Using MQTT keepalive: ${config.mqtt.keepalive}`);
options.keepalive = config.mqtt.keepalive;
}
if (config.mqtt.ca) {
log.debug(`MQTT SSL/TLS: Path to CA certificate = ${config.mqtt.ca}`);
options.ca = fs.readFileSync(config.mqtt.ca);
}
if (config.mqtt.key && config.mqtt.cert) {
log.debug(`MQTT SSL/TLS: Path to client key = ${config.mqtt.key}`);
log.debug(`MQTT SSL/TLS: Path to client certificate = ${config.mqtt.cert}`);
options.key = fs.readFileSync(config.mqtt.key);
options.cert = fs.readFileSync(config.mqtt.cert);
}
if (config.mqtt.user && config.mqtt.password) {
options.username = config.mqtt.user;
options.password = config.mqtt.password;
}
if (config.mqtt.client_id) {
log.debug(`Using MQTT client ID: '${config.mqtt.client_id}'`);
options.clientId = config.mqtt.client_id;
}
if (config.mqtt.reject_unauthorized !== undefined && !config.mqtt.reject_unauthorized) {
log.debug('MQTT reject_unauthorized set false, ignoring certificate warnings.');
options.rejectUnauthorized = false;
}
return options;
}
}
exports.Platform = Platform;
//# sourceMappingURL=platform.js.map