UNPKG

homebridge-z2m

Version:

Expose your Zigbee devices to HomeKit with ease, by integrating Zigbee2MQTT with Homebridge.

152 lines (139 loc) 4.71 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable no-console */ /* eslint-disable max-len */ import * as herdsman from 'zigbee-herdsman-converters'; import { ExposesEntry, exposesHasFeatures, exposesHasProperty } from './src/z2mModels'; // Filter out devices that only expose a `linkquality` // and add white label devices const allDevices = herdsman.definitions.filter( (d) => typeof d.exposes === 'function' || d.exposes.find((e) => e.name !== 'linkquality') !== undefined, ); for (const device of allDevices) { if (typeof device.exposes === 'function') { // Call function to generate array of exposes information. console.log(`Generating exposes array for ${device.vendor} ${device.model}`); device.exposes = device.exposes(); } if (device.whiteLabel) { for (const whiteLabel of device.whiteLabel) { const whiteLabelDevice = { ...device, model: whiteLabel.model, vendor: whiteLabel.vendor, description: whiteLabel.description, whiteLabelOf: device, }; delete whiteLabelDevice.whiteLabel; allDevices.push(whiteLabelDevice); } } } function generateZigbee2MqttLink(device: any) { const find = '[/| |:]'; const re = new RegExp(find, 'g'); return 'https://www.zigbee2mqtt.io/devices/' + encodeURIComponent(device.model.replace(re, '_')) + '.html'; } function getEndpointMapping(exposes: ExposesEntry[], previousEndpoint?: string | undefined): Record<string, Set<string>> { const mapping: Record<string, Set<string>> = {}; for (const e of exposes) { if (exposesHasFeatures(e)) { const propertyPrefix = e.property !== undefined ? `${e.property}.` : ''; const endpoint = e.endpoint === undefined ? previousEndpoint : e.endpoint; const subMapping = getEndpointMapping(e.features, endpoint); for (const [endpoint, properties] of Object.entries(subMapping)) { if (mapping[endpoint] === undefined) { mapping[endpoint] = new Set(); } for (const property of properties) { mapping[endpoint].add(propertyPrefix + property); } } // No need to check for properties again. continue; } if (exposesHasProperty(e)) { if (e.property === 'linkquality' || e.property === 'battery') { // Ignore this one continue; } let endpoint = e.endpoint === undefined ? previousEndpoint : e.endpoint; if (endpoint === undefined) { endpoint = 'UNDEFINED'; } if (mapping[endpoint] === undefined) { mapping[endpoint] = new Set(); } mapping[endpoint].add(e.property); } } return mapping; } function hasExposesWithNameWithoutEndpoint(exposes: ExposesEntry[]) { for (const e of exposes) { if (exposesHasProperty(e)) { if (e.property === 'linkquality') { // Ignore this one continue; } if (e.endpoint === undefined) { return true; } } // If this entry defines an endpoint, we assume the features inherit it. // TODO: update if statement if (exposesHasFeatures(e)) { if (e.endpoint !== undefined) { continue; } if (hasExposesWithNameWithoutEndpoint(e.features)) { return true; } } } return false; } function hasAtLeastOneDefinedEndpoint(exposes: ExposesEntry[]) { for (const e of exposes) { if (e.endpoint !== undefined) { return true; } if (exposesHasProperty(e)) { if (e.property === 'linkquality' || e.property === 'battery') { // Ignore this one continue; } if (e.endpoint !== undefined) { return true; } } if (exposesHasFeatures(e)) { if (hasAtLeastOneDefinedEndpoint(e.features)) { return true; } } } return false; } const devices: string[] = []; allDevices.forEach((d) => { try { if (d.whiteLabelOf === undefined) { if (hasAtLeastOneDefinedEndpoint(d.exposes) && hasExposesWithNameWithoutEndpoint(d.exposes)) { let entry = `* [${d.vendor} ${d.model}](${generateZigbee2MqttLink(d)}):`; const mapping = getEndpointMapping(d.exposes); const keys = Object.keys(mapping).sort(); for (const endpoint of keys) { entry += `\n * Endpoint \`${endpoint}\`: \`${Array.from(mapping[endpoint]).join('`, `')}\``; } devices.push(entry); } } } catch (Error) { console.log(`ERROR: ${d.vendor} ${d.model}: ${Error}`); } }); console.log('---------------'); console.log(`Devices with exposes without endpoint (${devices.length}):`); // Sort list alphabetically devices.sort(); devices.forEach((d) => console.log(d));