UNPKG

homebridge-daikin-oneplus

Version:
501 lines 28.1 kB
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'; import { DaikinOnePlusThermostat } from './platformThermostat.js'; import { DaikinOnePlusAQSensor } from './platformAQI.js'; import { DaikinOnePlusHumidity } from './platformHumidity.js'; import { DaikinOnePlusScheduleSwitch } from './platformScheduleSwitch.js'; import { DaikinOnePlusAwaySwitch } from './platformAwaySwitch.js'; import { DaikinOnePlusStateSwitch } from './platformStateSwitch.js'; import { DaikinApi } from './daikinapi.js'; import { DaikinOnePlusEmergencyHeatSwitch } from './platformEmergencyHeatSwitch.js'; import { DaikinOnePlusOneCleanFan } from './platformOneCleanFan.js'; import { DaikinOnePlusCirculateAirFan } from './platformCirculateAirFan.js'; import { DaikinOnePlusOutdoorTemperature } from './platformOutdoorTemperature.js'; import assert from 'node:assert'; /** * HomebridgePlatform * This class is the main constructor for your plugin, this is where you should * parse the user config and discover/register accessories with Homebridge. */ export class DaikinOnePlusPlatform { // this is used to track restored cached accessories accessories; api; config; log; Service; Characteristic; daikinApi; discoverTimer; constructor(log, config, api) { this.accessories = []; this.api = api; this.log = log; this.log.debug = this.debug.bind(this); this.Service = this.api.hap.Service; this.Characteristic = this.api.hap.Characteristic; //Don't start if not configured if (!config) { throw new Error('No configuration found.'); } assert(typeof config.user === 'string' && config.user.length > 0 && typeof config.password === 'string' && config.password.length > 0, 'No valid Daikin login credentials configured.'); assert(typeof config.name === 'string', 'No valid name configured.'); this.config = { debug: !!config.debug, user: config.user, password: config.password, name: config.name, includeDeviceName: !!config.includeDeviceName, enableEmergencyHeatSwitch: !!config.enableEmergencyHeatSwitch, enableOneCleanFan: !!config.enableOneCleanFan, enableCirculateAirFan: !!config.enableCirculateAirFan, enableScheduleSwitch: !!config.enableScheduleSwitch, enableAwaySwitch: !!config.enableAwaySwitch, enableStateSwitches: !!config.enableStateSwitches, ignoreIndoorAqi: !!config.ignoreIndoorAqi, ignoreOutdoorAqi: !!config.ignoreOutdoorAqi, ignoreIndoorHumSensor: !!config.ignoreIndoorHumSensor, ignoreOutdoorHumSensor: !!config.ignoreOutdoorHumSensor, ignoreThermostat: !!config.ignoreThermostat, ignoreOutdoorTemp: !!config.ignoreOutdoorTemp, autoResumeSchedule: !!config.autoResumeSchedule, logRaw: !!config.debug && !!config.logRaw, }; this.debug('Debug logging on. Expect lots of messages.'); this.debug('Using Include Device Name setting of %s.', this.config.includeDeviceName); this.debug('Finished initializing platform: %s', this.config.name); this.daikinApi = new DaikinApi(this.config.user, this.config.password, this.log, this.config.logRaw); // When this event is fired it means Homebridge has restored all cached accessories from disk. // Dynamic Platform plugins should only register new accessories after this event was fired, // in order to ensure they weren't added to homebridge already. This event can also be used // to start discovery of new accessories. this.api.on("didFinishLaunching" /* APIEvent.DID_FINISH_LAUNCHING */, this.discover.bind(this)); } discover() { this.log.debug('Executed didFinishLaunching callback'); clearTimeout(this.discoverTimer); //If initialized, no need to try and discover devices again. if (this.daikinApi.isInitialized()) { return; } // eslint-disable-next-line @typescript-eslint/no-misused-promises this.discoverTimer = setTimeout(async () => { try { // run the method to discover / register your devices as accessories await this.discoverDevices(); //Call discover again in case we failed to initialize the api and discover devices. this.discover(); } catch (error) { this.log.error('Discovery failed:', error); // Retry discovery on failure this.discover(); } }, 10 * 1000); } /** * This function is invoked when homebridge restores cached accessories from disk at startup. * It should be used to setup event handlers for characteristics and update respective values. */ configureAccessory(accessory) { this.log.info('Loading accessory from cache:', accessory.displayName); // add the restored accessory to the accessories cache so we can track if it has already been registered this.accessories.push(accessory); } /** * This is an example method showing how to register discovered accessories. * Accessories must only be registered once, previously created accessories * must not be registered again to prevent "duplicate UUID" errors. */ async discoverDevices() { if (!this.config.user && !this.config.password) { this.log.error('Credentials not set. Aborting discovery of devices.'); return; } await this.daikinApi.Initialize(); if (!this.daikinApi.isInitialized()) { this.log.error('Unable to retrieve devices. Aborting discovery of devices.'); return; } const devices = this.daikinApi.getDeviceList(); // loop over the discovered devices and register each one if it has not already been registered for (const device of devices) { this.log.info('Found device: %s', device.name); const deviceData = await this.daikinApi.getDeviceData(device.id); this.discoverThermostat(device); this.discoverOutdoorTemp(device); this.discoverOutdoorHumSensor(device); this.discoverIndoorHumSensor(device); this.discoverOutdoorAqi(device, deviceData); this.discoverIndoorAqi(device, deviceData); this.discoverScheduleSwitch(device); this.discoverAwaySwitch(device); this.discoverStateSwitches(device); this.discoverEmergencyHeatSwitch(device); this.discoverOneCleanFan(device); this.discoverCirculateAirFan(device); } } discoverEmergencyHeatSwitch(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_emergency_heat`); this.log.debug('Checking for Emergency Heat Switch...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (this.config.enableEmergencyHeatSwitch) { const dName = this.accessoryName(device, 'Emergency Heat'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing emergency heat switch from cache:', existingAccessory.displayName); new DaikinOnePlusEmergencyHeatSwitch(this, existingAccessory, device.id, this.daikinApi); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new emergency heat switch:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusEmergencyHeatSwitch(this, accessory, device.id, this.daikinApi); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing Emergency Heat switch this.log.debug('Removing emergency heat switch from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverOneCleanFan(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_one_clean_fan`); this.log.debug('Checking for One Clean Fan...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (this.config.enableOneCleanFan) { const dName = this.accessoryName(device, 'One Clean'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing one clean fan from cache:', existingAccessory.displayName); new DaikinOnePlusOneCleanFan(this, existingAccessory, device.id, this.daikinApi); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new one clean fan:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusOneCleanFan(this, accessory, device.id, this.daikinApi); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing one clean fan this.log.debug('Removing one clean fan from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverCirculateAirFan(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_circ_air_fan`); this.log.debug('Checking for Circulate Air Fan...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (this.config.enableCirculateAirFan) { const dName = this.accessoryName(device, 'Circulate Air'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing circulate air fan from cache:', existingAccessory.displayName); new DaikinOnePlusCirculateAirFan(this, existingAccessory, device.id, this.daikinApi); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new circulate air fan:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusCirculateAirFan(this, accessory, device.id, this.daikinApi); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing one clean fan this.log.debug('Removing circulate air fan from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverScheduleSwitch(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_schedule`); this.log.debug('Checking for Schedule Switch...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (this.config.enableScheduleSwitch) { const dName = this.accessoryName(device, 'Schedule State'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing schedule switch from cache:', existingAccessory.displayName); new DaikinOnePlusScheduleSwitch(this, existingAccessory, device.id, this.daikinApi); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new schedule switch:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusScheduleSwitch(this, accessory, device.id, this.daikinApi); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing Schedule switch this.log.debug('Removing Schedule switch from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverAwaySwitch(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_away`); this.log.debug('Checking for Away Switch...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (this.config.enableAwaySwitch) { const dName = this.accessoryName(device, 'Away State'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing away switch from cache:', existingAccessory.displayName); new DaikinOnePlusAwaySwitch(this, existingAccessory, device.id, this.daikinApi); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new away switch:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusAwaySwitch(this, accessory, device.id, this.daikinApi); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing Away switch this.log.debug('Removing Away switch from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverStateSwitches(device) { this.log.debug('Checking for System State Switches...'); this.discoverStateSwitch(device, 3 /* EquipmentStatus.HEATING */, 'Heating'); this.discoverStateSwitch(device, 1 /* EquipmentStatus.COOLING */, 'Cooling'); this.discoverStateSwitch(device, 4 /* EquipmentStatus.FAN */, 'Fan'); this.discoverStateSwitch(device, 2 /* EquipmentStatus.OVERCOOL_DEHUMIDIFYING */, 'Overcool'); this.discoverStateSwitch(device, 5 /* EquipmentStatus.IDLE */, 'Idle'); } discoverIndoorAqi(device, deviceData) { const uuid = this.api.hap.uuid.generate(`${device.id}_iaqi`); this.log.debug('Checking for indoor Air Quality sensor...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (!this.config.ignoreIndoorAqi) { const dName = this.accessoryName(device, 'Indoor AQI'); if (deviceData && deviceData.aqIndoorAvailable) { if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing indoor Air Quality sensor from cache:', existingAccessory.displayName); new DaikinOnePlusAQSensor(this, existingAccessory, device.id, this.daikinApi, true); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new indoor Air Quality sensor:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusAQSensor(this, accessory, device.id, this.daikinApi, true); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { this.log.debug('Removing legacy indoor Air Quality Sensor from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } else if (existingAccessory) { //Delete any existing Indoor AQI switch this.log.debug('Removing Indoor AQI sensor from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverOutdoorAqi(device, deviceData) { const uuid = this.api.hap.uuid.generate(`${device.id}_oaqi`); this.log.debug('Checking for outdoor Air Quality sensor...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (!this.config.ignoreOutdoorAqi) { const dName = this.accessoryName(device, 'Outdoor AQI'); if (deviceData && deviceData.aqOutdoorAvailable) { if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing outdoor Air Quality sensor from cache:', existingAccessory.displayName); new DaikinOnePlusAQSensor(this, existingAccessory, device.id, this.daikinApi, false); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new outdoor Air Quality sensor:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusAQSensor(this, accessory, device.id, this.daikinApi, false); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { this.log.debug('Removing legacy outdoor Air Quality Sensor from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } else if (existingAccessory) { //Delete any existing Outdoor AQI sensor this.log.debug('Removing Outdoor AQI sensor from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverIndoorHumSensor(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_ihum`); this.log.debug('Checking for indoor humidity sensor...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (!this.config.ignoreIndoorHumSensor) { const dName = this.accessoryName(device, 'Indoor Humidity'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing indoor humidity sensor from cache:', existingAccessory.displayName); new DaikinOnePlusHumidity(this, existingAccessory, device.id, this.daikinApi, true); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new indoor humidity sensor:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusHumidity(this, accessory, device.id, this.daikinApi, true); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing Indoor Humidity sensor this.log.debug('Removing Indoor Humidity sensor from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverOutdoorHumSensor(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_ohum`); this.log.debug('Checking for outdoor humidity sensor...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (!this.config.ignoreOutdoorHumSensor) { const dName = this.accessoryName(device, 'Outdoor Humidity'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing outdoor humidity sensor from cache:', existingAccessory.displayName); new DaikinOnePlusHumidity(this, existingAccessory, device.id, this.daikinApi, false); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new outdoor humidity sensor:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusHumidity(this, accessory, device.id, this.daikinApi, false); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing Outdoor Humidity sensor this.log.debug('Removing Outdoor Humidity sensor from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverThermostat(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_tstat`); this.log.debug('Checking for thermostat...'); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (!this.config.ignoreThermostat) { const dName = this.accessoryName(device, 'Thermostat'); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug('Restoring existing thermostat from cache:', existingAccessory.displayName); new DaikinOnePlusThermostat(this, existingAccessory, device.id, this.daikinApi); } else { // the accessory does not yet exist, so we need to create it this.log.debug('Adding new thermostat:', dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusThermostat(this, accessory, device.id, this.daikinApi); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { this.log.debug('Removing Thermostat from cache:', existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } discoverOutdoorTemp(device) { const uuid = this.api.hap.uuid.generate(`${device.id}_otemp`); this.log.debug('checking for Outdoor Temperature...'); const existingaccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (!this.config.ignoreOutdoorTemp) { const dname = this.accessoryName(device, 'Outdoor Temperature'); if (existingaccessory) { // the accessory already exists existingaccessory.displayName = dname; existingaccessory.context.device = device; this.log.debug('restoring existing Outdoor Temperature from cache:', existingaccessory.displayName); new DaikinOnePlusOutdoorTemperature(this, existingaccessory, device.id, this.daikinApi); } else { // the accessory does not yet exist, so we need to create it this.log.debug('adding new Outdoor Temperature:', dname); const accessory = new this.api.platformAccessory(dname, uuid); accessory.context.device = device; new DaikinOnePlusOutdoorTemperature(this, accessory, device.id, this.daikinApi); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingaccessory) { this.log.debug('removing Outdoor Temperature from cache:', existingaccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingaccessory]); } } discoverStateSwitch(device, state, name) { const uuid = this.api.hap.uuid.generate(`${device.id}_${name}_state`); this.log.debug(`Checking for system ${name} state switch...`); const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid); if (this.config.enableStateSwitches) { const dName = this.accessoryName(device, name); if (existingAccessory) { // the accessory already exists existingAccessory.displayName = dName; existingAccessory.context.device = device; this.log.debug(`Restoring existing system ${name} state switch from cache:`, existingAccessory.displayName); new DaikinOnePlusStateSwitch(this, existingAccessory, device.id, this.daikinApi, state); } else { // the accessory does not yet exist, so we need to create it this.log.debug(`Adding new system ${name} state switch:`, dName); const accessory = new this.api.platformAccessory(dName, uuid); accessory.context.device = device; new DaikinOnePlusStateSwitch(this, accessory, device.id, this.daikinApi, state); this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } else if (existingAccessory) { //Delete any existing state switch this.log.debug(`Removing system ${name} switch from cache:`, existingAccessory.displayName); this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); } } accessoryName(device, accessory) { return this.config.includeDeviceName ? `${device.name} ${accessory}` : accessory; } debug(message, ...parameters) { if (this.config.debug) { this.log.info(message, ...parameters); } } } //# sourceMappingURL=platform.js.map