homebridge-daikin-local-platform
Version:
Homebridge platform plugin providing HomeKit support for Daikin air conditioners with local control
198 lines (149 loc) • 6.51 kB
text/typescript
import {
API,
APIEvent,
Characteristic,
DynamicPlatformPlugin,
Logger,
PlatformAccessory,
PlatformConfig,
Service,
} from 'homebridge';
import {DaikinLocalAPI, DaikinDevice} from './daikin-local';
import ClimateAccessory from './accessories/climate';
import DaikinPlatformLogger from './logger';
import { DaikinAccessoryContext, DaikinPlatformConfig } from './types';
import {
PLATFORM_NAME,
PLUGIN_NAME,
} from './settings';
/**
* Daikin Local Platform Plugin for Homebridge
* Based on https://github.com/homebridge/homebridge-plugin-template
*/
export default class DaikinPlatform implements DynamicPlatformPlugin {
public readonly Service: typeof Service = this.api.hap.Service;
public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic;
// Used to track restored cached accessories
private readonly accessories: PlatformAccessory<DaikinAccessoryContext>[] = [];
public readonly daikinLocalAPI: DaikinLocalAPI;
public readonly log: DaikinPlatformLogger;
protected readonly DaikinDevices: Record<string, DaikinDevice> = {};
public platformConfig: DaikinPlatformConfig;
/**
* This constructor is where you should parse the user config
* and discover/register accessories with Homebridge.
*
* @param logger Homebridge logger
* @param config Homebridge platform config
* @param api Homebridge API
*/
constructor(
homebridgeLogger: Logger,
config: PlatformConfig,
public readonly api: API,
) {
this.platformConfig = config as DaikinPlatformConfig;
// Initialise logging utility
this.log = new DaikinPlatformLogger(homebridgeLogger, this.platformConfig.debugMode);
this.daikinLocalAPI = new DaikinLocalAPI(
this.log,
);
/**
* 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(APIEvent.DID_FINISH_LAUNCHING, () => {
this.log.debug('Finished launching and restored cached accessories.');
this.configurePlugin();
});
}
async configurePlugin() {
await this.checkDevices();
}
async checkDevices() {
if (!this.platformConfig.climateIPs || this.platformConfig.climateIPs.length === 0) {
this.log.error('No climate IPs configured - aborting plugin start. ');
return;
}
await this.daikinLocalAPI.fetchDevices(this.platformConfig.climateIPs).then((devices) => {
for(let i = 0; i < devices.length; i++) {
try {
if(!devices[i].getMacAddress()) {
this.log.error(`Device ${devices[i].getDeviceName()} has no MAC address - skipping.`);
continue;
}
const uuid = this.api.hap.uuid.generate(devices[i].getMacAddress());
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
this.DaikinDevices[devices[i].getMacAddress()] = devices[i];
if (existingAccessory !== undefined) {
// The accessory already exists
this.log.info(`Restoring accessory '${existingAccessory.displayName}' `
+ `(${devices[i].getMacAddress()}) from cache.`);
// If you need to update the accessory.context then you should run
// `api.updatePlatformAccessories`. eg.:
existingAccessory.context.device = devices[i];
this.api.updatePlatformAccessories([existingAccessory]);
// Create the accessory handler for the restored accessory
this.createDaikinAccessory(devices[i], this, existingAccessory);
} else {
this.log.info(`Adding accessory '${devices[i].getDeviceName()}' (${devices[i].getMacAddress()}).`);
// The accessory does not yet exist, so we need to create it
const accessory = new this.api.platformAccessory<DaikinAccessoryContext>(
devices[i].getDeviceName(), uuid);
// Store a copy of the device object in the `accessory.context` property,
// which can be used to store any data about the accessory you may need.
accessory.context.device = devices[i];
// Create the accessory handler for the newly create accessory
// this is imported from `platformAccessory.ts`
this.createDaikinAccessory(devices[i], this, accessory);
// Link the accessory to your platform
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
}
}
catch (e) {
this.log.error(`Error registering device ${devices[i].getDeviceName()}: ${e}`);
continue;
}
}
});
for (const cachedAccessory of this.accessories) {
if (cachedAccessory.context.device) {
try{
const guid = cachedAccessory.context.device.getMacAddress();
const daikinDevice = this.DaikinDevices[guid];
if (daikinDevice === undefined) {
this.log.info(`Removing accessory '${cachedAccessory.displayName}' (${guid}) `
+ 'because it does not exist on the config.');
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [cachedAccessory]);
}
}catch (e) {
this.log.error(`Error removing stale accessory ${cachedAccessory.displayName}: ${e}`);
continue;
}
}
}
}
/**
* This function is invoked when Homebridge restores cached accessories from disk at startup.
* It should be used to set up event handlers for characteristics and update respective values.
*/
configureAccessory(accessory: PlatformAccessory<DaikinAccessoryContext>) {
this.log.info(`Loading accessory '${accessory.displayName}' from cache.`);
/**
* We don't have to set up the handlers here,
* because our device discovery function takes care of that.
*
* But we need to add the restored accessory to the
* accessories cache so we can access it during that process.
*/
this.accessories.push(accessory);
}
createDaikinAccessory(
device: DaikinDevice,
platform: DaikinPlatform,
accessory: PlatformAccessory<DaikinAccessoryContext>) {
new ClimateAccessory(platform, accessory);
}
}