UNPKG

@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
"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