@wanderxjtu/homebridge-yeelighter
Version:
Yeelight support for Homebridge with particular support of ceiling lights
217 lines • 13.6 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.YeelighterPlatform = void 0;
const node_url_1 = __importDefault(require("node:url"));
const settings_1 = require("./settings");
const yeedevice_1 = require("./yeedevice");
const yeeaccessory_1 = require("./yeeaccessory");
const discovery_1 = require("./discovery");
const yeeaccessory_2 = require("./yeeaccessory");
/**
* 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.
*/
class YeelighterPlatform {
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.AdaptiveLightingController = this.api.hap.AdaptiveLightingController;
// this is used to track restored cached accessories
this.accessories = [];
// called when a Yeelight has responded to the discovery query
this.onDeviceDiscovery = (detectedInfo) => {
var _a;
try {
if (!detectedInfo.id) {
this.log.warn("ingoring device with corrupt DeviceInfo", detectedInfo);
return;
}
const trackedAttributes = yeeaccessory_2.TRACKED_ATTRIBUTES; // .filter(attribute => supportedAttributes.includes(attribute));
const override = this.config.override || [];
const overrideConfig = override.find(item => item.id === detectedInfo.id);
const separateAmbient = ((((_a = this.config) === null || _a === void 0 ? void 0 : _a.split) && (overrideConfig === null || overrideConfig === void 0 ? void 0 : overrideConfig.separateAmbient) !== false) || (overrideConfig === null || overrideConfig === void 0 ? void 0 : overrideConfig.separateAmbient) === true)
&& (detectedInfo.support.includes("bg_set_power") || !!(overrideConfig === null || overrideConfig === void 0 ? void 0 : overrideConfig.backgroundLight));
const newDeviceInfo = {
...detectedInfo,
trackedAttributes,
};
// generate a unique id for the accessory this should be generated from
// something globally unique, but constant, for example, the device serial
// number or MAC address
const uuid = this.api.hap.uuid.generate(newDeviceInfo.id);
// if the device has a secondary (ambient) light and is configured to have
// this shown as a separate top-level light, generate a separate UUID for it
const ambientUuid = this.api.hap.uuid.generate(`${newDeviceInfo.id}#ambient`);
if (overrideConfig) {
this.log.info(`Override config for ${detectedInfo.id}: ${JSON.stringify(overrideConfig)}`);
if (overrideConfig.ignored) {
this.log.info(`Ignoring ${detectedInfo.id} as configured.`);
// see if an accessory with the same uuid has already been registered and restored from
// the cached devices we stored in the `configureAccessory` method above
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
const purgeList = [];
if (existingAccessory) {
this.log.info("Removing ignored accessory from cache:", existingAccessory.displayName);
purgeList.push(existingAccessory);
}
const existingAmbientAccessory = this.accessories.find(accessory => accessory.UUID === ambientUuid);
if (existingAmbientAccessory) {
purgeList.push(existingAmbientAccessory);
this.log.info("Removing ignored ambient accessory from cache:", existingAmbientAccessory.displayName);
}
if (purgeList.length > 0) {
try {
for (const item of purgeList) {
const index = this.accessories.indexOf(item);
if (index >= 0) {
this.accessories.splice(index, 1);
}
}
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, purgeList);
}
catch (error) {
this.log.warn("Failed to unregister", purgeList, error);
}
}
return;
}
}
const device = new yeedevice_1.Device(newDeviceInfo);
// see if an accessory with the same uuid has already been registered and restored from
// the cached devices we stored in the `configureAccessory` method above
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
let ambientAccessory = this.accessories.find(accessory => accessory.UUID === ambientUuid);
if (ambientAccessory && !separateAmbient) {
try {
this.log.info(`Separate Ambient Accessory not wanted anymore. Unregistering`, ambientAccessory.UUID);
const index = this.accessories.indexOf(ambientAccessory);
if (index >= 0) {
this.accessories.splice(index, 1);
}
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [ambientAccessory]);
// TODO: remove from this.accessories
}
catch (error) {
this.log.warn("failed to unregister", ambientAccessory.UUID, error);
}
ambientAccessory = undefined;
}
if (existingAccessory) {
// the accessory already exists
if (device) {
this.log.info(`New (cached) ${newDeviceInfo.model} [${newDeviceInfo.id}] found at ${newDeviceInfo.location}`);
// update the accessory.context
existingAccessory.context.device = newDeviceInfo;
const updateAccessories = [existingAccessory];
if (!ambientAccessory && separateAmbient) {
ambientAccessory = new this.api.platformAccessory(newDeviceInfo.id, ambientUuid);
ambientAccessory.context.device = newDeviceInfo;
this.log.info(`Separate Ambient Accessory created with UUID ${ambientUuid}`);
// link the accessory to your platform
this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [ambientAccessory]);
updateAccessories.push(ambientAccessory);
}
yeeaccessory_1.YeeAccessory.instance(device, this, existingAccessory, ambientAccessory);
// update accessory cache with any changes to the accessory details and information
this.api.updatePlatformAccessories(updateAccessories);
}
else {
// it is possible to remove platform accessories at any time using `api.unregisterPlatformAccessories`, eg.:
// remove platform accessories when no longer present
try {
this.log.info("Removing existing accessory from cache:", existingAccessory.displayName);
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [existingAccessory]);
const index = this.accessories.indexOf(existingAccessory);
if (index >= 0) {
this.accessories.splice(index, 1);
}
}
catch (error) {
this.log.warn("Failed to remove accessory", existingAccessory, error);
}
}
}
else {
const addedAccessories = [];
// the accessory does not yet exist, so we need to create it
this.log.info(`New ${newDeviceInfo.model} [${newDeviceInfo.id}] found at ${newDeviceInfo.location}`);
const accessory = new this.api.platformAccessory(newDeviceInfo.id, uuid);
// store a copy of the device object in the `accessory.context`
// the `context` property can be used to store any data about the accessory you may need
accessory.context.device = newDeviceInfo;
this.configureAccessory(accessory);
addedAccessories.push(accessory);
this.log.info(`Accessory created with UUID ${uuid}`);
if (separateAmbient && !ambientAccessory) {
ambientAccessory = new this.api.platformAccessory(newDeviceInfo.id, ambientUuid);
ambientAccessory.context.device = newDeviceInfo;
this.configureAccessory(ambientAccessory);
addedAccessories.push(ambientAccessory);
this.log.info(`Separate Ambient Accessory created with UUID ${ambientUuid}`);
}
// create the accessory handler for the newly create accessory
yeeaccessory_1.YeeAccessory.instance(device, this, accessory, ambientAccessory);
// link the accessory to your platform
this.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, addedAccessories);
}
}
catch (error) {
this.log.error("Device discovery handling failed", error);
}
};
this.log.debug("Finished initializing platform:", this.config.name);
this.agent = new discovery_1.Discovery();
this.agent.on("started", () => {
this.log.debug("** Discovery Started **");
});
this.agent.on("didDiscoverDevice", this.onDeviceDiscovery);
// 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", () => {
this.log.debug("Executed didFinishLaunching callback");
this.agent.listen();
this.addHardCodedAccessories();
});
}
/**
* 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) {
var _a, _b, _c, _d;
if (this.accessories.some(a => a.UUID === accessory.UUID)) {
this.log.warn(`Ingnoring duplicate accessory from cache: ${accessory.displayName} (${((_b = (_a = accessory.context) === null || _a === void 0 ? void 0 : _a.device) === null || _b === void 0 ? void 0 : _b.model) || "unknown"})`);
return;
}
this.log.info(`Loading accessory from cache: ${accessory.displayName} (${((_d = (_c = accessory.context) === null || _c === void 0 ? void 0 : _c.device) === null || _d === void 0 ? void 0 : _d.model) || "unknown"})`);
// add the restored accessory to the accessories cache so we can track if it has already been registered
this.accessories.push(accessory);
}
addHardCodedAccessories() {
var _a;
const manualAccessories = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.manual) || [];
this.log.info(`adding ${manualAccessories.length} manual accessories`);
for (const manualAccessory of manualAccessories) {
const deviceInfo = { ...yeedevice_1.EMPTY_DEVICEINFO };
deviceInfo.location = `yeelight://${manualAccessory.address}`;
const parsedUrl = node_url_1.default.parse(deviceInfo.location);
deviceInfo.host = parsedUrl.hostname || "";
deviceInfo.port = Number(parsedUrl.port || "55443");
deviceInfo.id = manualAccessory.id;
deviceInfo.model = manualAccessory.model;
deviceInfo.support = manualAccessory.support;
this.onDeviceDiscovery(deviceInfo);
}
}
}
exports.YeelighterPlatform = YeelighterPlatform;
//# sourceMappingURL=platform.js.map