UNPKG

homebridge-harmony

Version:

Publish your harmony activities as homekit accessories

250 lines (218 loc) 7.61 kB
import HarmonyConst from './harmonyConst.js'; const UNSAFE_OBJECT_KEYS = new Set(['__proto__', 'prototype', 'constructor']); const MAX_MACRO_COMMANDS = 100; const MAX_MACRO_DELAY = 10000; const HarmonyTools = { isSafeObjectKey: function (key) { return typeof key === 'string' && key.length > 0 && !UNSAFE_OBJECT_KEYS.has(key); }, isNil: function (value) { return value === undefined || value === null; }, isNotNil: function (value) { return value !== undefined && value !== null; }, safeFileNameSegment: function (value) { const segment = String(this.isNil(value) ? 'unknown' : value) .replace(/[\\/\0]/g, '_') .trim(); return segment.length > 0 ? segment.slice(0, 128) : 'unknown'; }, parseJsonObject: function (value, fallback = {}) { try { const parsed = JSON.parse(String(value)); if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { return fallback; } return Object.entries(parsed).reduce((result, [key, entryValue]) => { if (this.isSafeObjectKey(key)) { result[key] = entryValue; } return result; }, Object.create(null)); } catch (err) { return fallback; } }, parseJsonArray: function (value, fallback = []) { try { const parsed = JSON.parse(String(value)); return Array.isArray(parsed) ? parsed : fallback; } catch (err) { return fallback; } }, isPlatformWithSwitch(platform) { if ( platform.showTurnOffActivity || platform.publishGeneralVolumeSlider || platform.switchAccessories || (platform.activitiesToPublishAsAccessoriesSwitch && platform.activitiesToPublishAsAccessoriesSwitch.length > 0) || (platform.sequencesToPublishAsAccessoriesSwitch && platform.sequencesToPublishAsAccessoriesSwitch.length > 0) || (platform.devicesToPublishAsAccessoriesSwitch && platform.devicesToPublishAsAccessoriesSwitch.length > 0) || (platform.homeControlsToPublishAsAccessoriesSwitch && platform.homeControlsToPublishAsAccessoriesSwitch.length > 0) ) { return true; } else { return false; } }, isPlatformEmpty(platform) { if (platform.TVAccessory || this.isPlatformWithSwitch(platform)) { return false; } else { return true; } }, checkParameter: function (parameter, def) { if (this.isNil(parameter)) { return def; } else { if (typeof parameter === 'string') { switch (parameter.toLowerCase().trim()) { case 'true': case 'yes': return true; case 'false': case 'no': case null: return false; default: return parameter; } } else { return parameter; } } }, transformActivityIdToActiveIdentifier: function (currentInputService, sources) { if (this.isNotNil(currentInputService) && currentInputService.activityId > 0) { for (let i = 0, len = sources.length; i < len; i++) { if (sources[i].activityId == currentInputService.activityId) return i + 1; } } else return 0; }, transformActiveIdentifierToActivityId: function (activeIdentifier, sources) { if (sources.length >= activeIdentifier && activeIdentifier > 0) { return sources[activeIdentifier - 1].activityId; } else return -1; }, checkTurnOffActivityOption: function (str) { if (this.isNil(str)) return false; if (typeof str === 'boolean') { return str === true; } if (typeof str === 'string') { switch (str.toLowerCase().trim()) { case 'true': case 'yes': case '1': return true; case 'false': case 'no': case '0': case null: return false; default: return str; } } }, serviceIsNotTv(service) { return ( service.type === HarmonyConst.DEVICE_TYPE || service.type === HarmonyConst.DEVICEMACRO_TYPE || service.type === HarmonyConst.SEQUENCE_TYPE || service.type === HarmonyConst.HOME_TYPE ); }, isActivtyToBeSkipped: function (platform, activity) { return ( platform.addAllActivitiesToSkippedIfSameStateActivitiesList || (platform.skippedIfSameStateActivities && platform.skippedIfSameStateActivities.includes(activity)) ); }, processCommands: async function (hb, platform, commands) { const safeCommands = Array.isArray(commands) ? commands.slice(0, MAX_MACRO_COMMANDS) : []; for (const command of safeCommands) { if (typeof command !== 'string' || command.length === 0) { continue; } let commandTosend = command.split('|'); let timeToWait = Number(commandTosend.length === 2 ? commandTosend[1] : NaN); if (!Number.isFinite(timeToWait)) timeToWait = HarmonyConst.DELAY_FOR_MACRO; timeToWait = Math.max(0, Math.min(timeToWait, MAX_MACRO_DELAY)); await processCommand(hb, platform, commandTosend[0], timeToWait); } }, disablePreviousActivity: function (platform, characteristic, service, commandToSend, on) { //we disable previous activities that were on if (service.activityId != -1 && service.activityId != commandToSend && on) { platform.log.debug('(' + platform.name + ')' + 'Switching off ' + service.displayName); characteristic.updateValue(false); } }, handleOffActivity: function (platform, characteristic, service, commandToSend) { //we turn off Off Activity if another activity was launched if (service.activityId == -1 && commandToSend != -1) { platform.log.debug( '(' + platform.name + ')' + 'New activity on , turning off off Activity ' + service.displayName ); characteristic.updateValue(platform.showTurnOffActivity == 'inverted' ? true : false); } //we turn on Off Activity if we turned off an activity (or turn on the general switch) if (service.activityId == -1 && commandToSend == -1) { platform.log.debug( '(' + platform.name + ')' + 'Turning on off Activity ' + service.displayName ); characteristic.updateValue( platform.showTurnOffActivity != 'inverted' && platform.showTurnOffActivity != 'stateless' ? true : false ); } }, isCommandOk: function (data) { return data && data.code && data.code == 200 && data.msg && data.msg == 'OK'; }, isCommandInProgress: function (data) { return data && (data.code == 202 || data.code == 100); }, /** * Resets a characteristic of a service by using a getter callback with a * specific delay * * @param {Service} service The service * @param {Characteristic} characterisitc The characteristic of the service to reset * @param {number} delay The delay when the reset takes place */ resetCharacteristic: function (service, characteristic, delay) { if (!delay) { delay = 1000; } setTimeout(function () { service.getCharacteristic(characteristic).emit('get', function (error, newValue) { service.getCharacteristic(characteristic).updateValue(newValue); }); }, delay); }, }; async function processCommand(hb, platform, command, timeToWait) { // notice that we can await a function // that returns a promise await hb.sendCommand(platform, command); await delay(timeToWait); } function delay(timeToWait) { return new Promise((resolve) => setTimeout(resolve, timeToWait)); } export default HarmonyTools;