Repository: ztalbot2000/homebridge-cmd4 Branch: master Commit: 85928c56f4fe Files: 136 Total size: 2.1 MB Directory structure: gitextract_pta7yrh3/ ├── .eslintrc.json ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.md │ │ ├── feature-request.md │ │ └── support-request.md │ └── pull_request_template.md ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── Cmd4Accessory.js ├── Cmd4Platform.js ├── Cmd4PriorityPollingQueue.js ├── Extras/ │ ├── Cmd4Scripts/ │ │ ├── CheckYourScript.sh │ │ ├── Examples/ │ │ │ ├── AirPurifier.js │ │ │ ├── AnyDevice │ │ │ ├── DoorLock.sh │ │ │ ├── ExampleJavaScript_template.js │ │ │ ├── ExampleShellScript_template.sh │ │ │ ├── PS4.sh │ │ │ ├── PS5.sh │ │ │ ├── SecuritySystem.js │ │ │ ├── advanced_ping.sh │ │ │ ├── basic_ping.sh │ │ │ ├── middleWare.sh │ │ │ └── wakeonlan.sh │ │ └── State.js │ ├── config.json │ ├── config.min.json │ └── jsmin.c ├── LICENSE ├── README.md ├── RUNNING_CHANGELOG.md ├── cmd4Constants.js ├── cmd4Settings.js ├── commitlint.config.js ├── docs/ │ ├── AdvancedTroubleShooting.md │ ├── Developers.md │ ├── autoGenerated/ │ │ └── CMD4_AccessoryDescriptions.html │ └── index.html ├── index.js ├── lib/ │ ├── CMD4_ACC_TYPE_ENUM.js │ ├── CMD4_CHAR_TYPE_ENUMS.js │ └── CMD4_DEVICE_TYPE_ENUM.js ├── package.json ├── postinstall.js ├── test/ │ ├── CMD4_ACC_TYPE_ENUM.js │ ├── CMD4_CHAR_TYPE_ENUMS.js │ ├── CMD4_DEVICE_TYPE_ENUM.js │ ├── Cmd4Accessory.js │ ├── Cmd4AccessoryGetValue.js │ ├── Cmd4AccessorySetValue.js │ ├── Cmd4Mode.js │ ├── Cmd4Platform.js │ ├── Cmd4PlatformRestartTests.js │ ├── Cmd4PriorityPollingQueue.js │ ├── Cmd4Storage.js │ ├── HV.js │ ├── Logger.js │ ├── VariableTimer.js │ ├── allTests │ ├── async-dump.js │ ├── cmd4Constants.js │ ├── configHasCharacteristicProps.js │ ├── configTest.js │ ├── echoScripts/ │ │ ├── echo_0 │ │ ├── echo_1 │ │ ├── echo_ACTIVE │ │ ├── echo_DISABLED │ │ ├── echo_ENABLED │ │ ├── echo_INACTIVE │ │ ├── echo_On │ │ ├── echo_after5seconds │ │ ├── echo_errorToStderr │ │ ├── echo_false │ │ ├── echo_nothing │ │ ├── echo_null │ │ ├── echo_nullAndErrorToStderr │ │ ├── echo_quoted0 │ │ ├── echo_quoted1 │ │ ├── echo_quotedFALSE │ │ ├── echo_quotedNULL │ │ ├── echo_quotedNothing │ │ ├── echo_quotedTRUE │ │ ├── echo_too_much │ │ ├── echo_true │ │ ├── echo_true_withRcOf1 │ │ ├── justExitWithRCof0 │ │ ├── justExitWithRCof1 │ │ ├── runToTimeoutRcOf0 │ │ ├── runToTimeoutRcOf1 │ │ └── testGetSetValues.js │ ├── extractKeyValue.js │ ├── fakeGato.js │ ├── getAccessoryNameFunctions.js │ ├── getAccessoryUUID.js │ ├── getSetAllValues.js │ ├── indexOfEnum.js │ ├── initPluginTest.js │ ├── internalRelatedTargetTests.js │ ├── isAccDirective.js │ ├── isCmd4Directive.js │ ├── isDevDirective.js │ ├── isJSON.js │ ├── isNumeric.js │ ├── loadPluginTest.js │ ├── mocha-setup │ ├── pollingTest.js │ ├── sanityTests │ ├── systemTest.js │ ├── testAdvAirGetSet.js │ ├── testOurConfig.json.js │ ├── transposeCMD4Props.js │ ├── trueTypeOf.js │ └── versionChecker.js ├── tools/ │ ├── Cmd4AccDocGenerator │ ├── generateChangeLog │ └── whereIsConstant └── utils/ ├── Cmd4Storage.js ├── HV.js ├── Logger.js ├── VariableTimer.js ├── createAccessorysInformationService.js ├── extractKeyValue.js ├── getAccessoryNameFunctions.js ├── getAccessoryUUID.js ├── indexOfEnum.js ├── indexOfEnumLintTest.js ├── isAccDirective.js ├── isCmd4Directive.js ├── isDevDirective.js ├── isJSON.js ├── isNumeric.js ├── lcFirst.js ├── transposeCMD4Props.js ├── trueTypeOf.js ├── ucFirst.js └── versionChecker.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "env": { "browser": false, "commonjs": false, "node": true, "mocha": true, "es2021": true }, "globals": { "assert" : "writeable", "expect" : "writeable", "sinon" : "writeable", "ACC_EOL" : "readonly", "DEVICE_EOL" : "readonly", "FORMAT_EOL" : "readonly", "UNITS_EOL" : "readonly", "PERMS_EOL" : "readonly", "DEVICE_DATA" : "readonly", "ACC_DATA" : "readonly", "CHAR_DATA" : "readonly", "CMD4_CHAR_TYPE_ENUMS" : "readonly", "CMD4_ACC_TYPE_ENUM" : "readonly", "CMD4_DEVICE_TYPE_ENUM" : "readonly", "cleanStatesDir" : "readonly", "accEnumIndexToC" : "readonly", "devEnumIndexToC" : "readonly", "fs" : "writeable", "HomebridgeAPI" : "writeable", "Logger" : "writeable", "platformAccessory_1" : "writeable" }, "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 12 }, "rules": { "no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted"}], "no-whitespace-before-property": ["error"], "arrow-spacing": ["error"] } } ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.md ================================================ --- name: Bug Report about: Raise a bug related report for cmd4. title: "[Bug]" labels: bug assignees: ztalbot2000 --- ** Cmd4 No longer supported:** **Describe The Bug:** **To Reproduce:** **Expected Behaviour:** [**Link to Logs:**]() **Paste of Logs:** ``` ``` **Cmd4 Config:** ```json ``` **Screenshots:** **Environment:** * **Node.js Version**: * **NPM Version**: * **Homebridge Version**: * **homebridge-cmd4 Version**: * **Operating System**: Raspbian / Ubuntu / Debian / Windows / macOS / Docker / other * **Process Supervisor**: Systemd / init.d / pm2 / launchctl / Docker / hb-service / other / none ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.md ================================================ --- name: Feature Request about: Suggest an idea or improvement for cmd4. title: "[Feature Request]" labels: enhancement assignees: ztalbot2000 --- ** Cmd4 No longer supported:** **Is your feature request related to a problem? Please describe:** **Describe the solution you'd like:** **Describe alternatives you've considered:** **Additional context:** ================================================ FILE: .github/ISSUE_TEMPLATE/support-request.md ================================================ --- name: Support Request about: Stuck on one of the installation steps or having trouble with your script? title: "[Support]" labels: help wanted assignees: ztalbot2000 --- ** Cmd4 No longer supported:** **Describe Your Problem:** [**Link to Logs:**]() **Paste of Logs:** ``` ``` **Cmd4 Config:** ```json ``` **Screenshots:** **Environment:** * **Node.js Version**: * **NPM Version**: * **Homebridge Version**: * **homebridge-cmd4 Version**: * **Operating System**: Raspbian / Ubuntu / Debian / Windows / macOS / Docker / other * **Process Supervisor**: Systemd / init.d / pm2 / launchctl / Docker / hb-service / other / none ================================================ FILE: .github/pull_request_template.md ================================================ --- name: Pull Request about: Resolve an issue or add an improvement to cmd4. title: "[Pull Request]" labels: pull-request assignees: ztalbot2000 --- **Is your pull request related to a problem? Please describe:** **Describe the solution you'd have implemented:** **Do your changes pass unit testing ( npm run test ) :** - [x] Yes - [ ] No **Do your changes pass lint testing ( npm run lint ) :** - [x] Yes - [ ] No **Additional context:** ================================================ FILE: .gitignore ================================================ # Mac .DS_Store # Text editors *.swp # Stupid commits of npm package *.tgz # My favorite temp backup *.[0-9] *.save* *_save* *_bak* # Compiled Executables jsmin a.out # node node_modules npm-debug.log .node-version test/tmp/* # Not a fan package-lock.json # work in progress folder local ================================================ FILE: .npmignore ================================================ .eslintrc.json .gitignore .github .huskyrc .DS_Store *_save* *_bak* *.swp commitlint.config.js jsmin utils/indexOfEnumLintTest.js screenshots test node_modules local tools docs/autoGenerated docs/images ================================================ FILE: CHANGELOG.md ================================================ # Homebridges-cmd4 - CMD4 Plugin for Homebridge - Supports ~All Accessory Types and now all Characteristics too!! #### 8.0.4 (2026-02-03) ##### Bug Fixes * Support Node v24 ([feb476b5](https://github.com/ztalbot2000/homebridge-cmd4/commit/feb476b5bbea98099f7ebaf4a8dcea03572dac82)) ================================================ FILE: Cmd4Accessory.js ================================================ 'use strict'; const moment = require( "moment" ); // Settings, Globals and Constants let settings = require( "./cmd4Settings" ); const constants = require( "./cmd4Constants" ); let Logger = require( "./utils/Logger" ); const { getAccessoryName, getAccessoryDisplayName } = require( "./utils/getAccessoryNameFunctions" ); let getAccessoryUUID = require( "./utils/getAccessoryUUID" ); const { addQueue, queueExists } = require( "./Cmd4PriorityPollingQueue" ); // Hierarchy variables let HV = require( "./utils/HV" ); let createAccessorysInformationService = require( "./utils/createAccessorysInformationService" ); let lcFirst = require( "./utils/lcFirst" ); let trueTypeOf = require( "./utils/trueTypeOf" ); // The sObject.defineProperty is to resolve a lint issue. // See utils/indexOfEnumLintTest.js for further information. let indexOfEnum = require( "./utils/indexOfEnum" ); Object.defineProperty( exports, "indexOfEnum", { enumerable: true, get: function ( ){ return indexOfEnum.indexOfEnum; } }); // For changing validValue Constants to Values and back again var { transposeConstantToValidValue, } = require( "./utils/transposeCMD4Props" ); let isJSON = require( "./utils/isJSON" ); let isCmd4Directive = require( "./utils/isCmd4Directive" ); let isAccDirective = require( "./utils/isAccDirective" ); let isDevDirective = require( "./utils/isDevDirective" ); // Pretty Colors var chalk = require( "chalk" ); // These would already be initialized by index.js let CMD4_ACC_TYPE_ENUM = require( "./lib/CMD4_ACC_TYPE_ENUM" ).CMD4_ACC_TYPE_ENUM; let CMD4_DEVICE_TYPE_ENUM = require( "./lib/CMD4_DEVICE_TYPE_ENUM" ).CMD4_DEVICE_TYPE_ENUM; const Cmd4Storage = require( "./utils/Cmd4Storage" ); let FakeGatoHistoryService = null; // Only one TV is allowed per bridge. Circumvented by // publishing the TV externally. let numberOfTVsPerBridge = 0; // Array Remove function removeFromArray( arr, val ) { for (let i = arr.length - 1; i >= 0; i--) { if (arr[i] === val) { // console.log("Removing %s", val ); arr.splice(i, 1); } } return arr; } // Accessory definitions - THE GOOD STUFF STARTs HERE // // An Homebridge accessory by default is passed the following params // // @params: // log - Logging functionality. // config - The JSON description of the accessory. // api - Homebridge API. // // @Optional params // parentInfo - Optionally passed from a parent as if this is a linked accessory, // or from a CMD4 Platform. // // class Cmd4Accessory { constructor( log, config, api, STORED_DATA_ARRAY, parentInfo ) { // Non Platform accessories get called with homebridges Logger // replace with ours if ( typeof log.setOutputEnabled === "function" ) { this.log = log; // Carry the debug flag from the platform settings.cmd4Dbg = log.debugEnabled; } else { this.log = new Logger( ); if ( config[ constants.DEBUG ] == true || config[ "Debug" ] == true || process.env.DEBUG == settings.PLATFORM_NAME ) { settings.cmd4Dbg = true; } } this.log.setDebugEnabled( settings.cmd4Dbg ); this.config = config; this.api = api; // keep a copy because traversing it for format checking can be slow. this.Characteristic = api.hap.Characteristic; this.parentInfo = parentInfo; // Use parent values ( if any ) or these defaults. // LEVEL is a number, possibly 0 which must be handled more precisely. this.CMD4 = ( parentInfo && parentInfo.CMD4 ) ? parentInfo.CMD4 : constants.STANDALONE; this.LEVEL = ( parentInfo && parentInfo.LEVEL !== undefined ) ? parentInfo.LEVEL + 1 : 0; this.createdCmd4Accessories = ( parentInfo && parentInfo.createdCmd4Accessories ) ? parentInfo.createdCmd4Accessories : [ ]; let typeMsg = [ "", "Linked ", "Added " ][ this.LEVEL ] || ""; if ( settings.cmd4Dbg ) log.debug( chalk.blue ( `Creating ${ typeMsg }${ this.CMD4 } Accessory type for : ${ config.displayName } LEVEL: ${ this.LEVEL }` ) ); this.services = [ ]; this.linkedAccessories = [ ]; this.listOfVariables = { }; this.listOfConstants = { }; // Determines if the accessory is communicable this.errorValue = 0; this.errorString = "init"; // Used to determine missing related characteristics and // to determine if the related characteristic is also polled. this.listOfPollingCharacteristics = { }; // An extra flag this.ServiceCreated = false; // DisplayName and/or Name must be defined. // No need to update config anymore as it is no longer cached, only the Characteristic values are. this.name = getAccessoryName( this.config ); this.displayName = getAccessoryDisplayName( this.config ); // Everything that needs to talk to the device now goes through the queue this.queue = null; // Use the Hierarhy variables from the parent, if not create it. this.hV = new HV( ); if ( parentInfo && parentInfo.hV ) { this.hV.update( parentInfo.hV ); } // In case it is not passed in. if ( STORED_DATA_ARRAY == undefined || STORED_DATA_ARRAY == null ) this.STORED_DATA_ARRAY = [ ]; else this.STORED_DATA_ARRAY = STORED_DATA_ARRAY; let parseConfigShouldUseCharacteristicValues = true; if ( ! Array.isArray( this.STORED_DATA_ARRAY ) ) { this.log.warn( "STORED_DATA_ARRAY passed in is not an array and should be reported." ); this.STORED_DATA_ARRAY = [ ]; } // 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. let uuid = getAccessoryUUID( config, this.api.hap.uuid ); // Handle case change let existingDataU = this.STORED_DATA_ARRAY.find( data => data[ "UUID" ] === uuid ); if ( existingDataU ) { //Z this.log.info( chalk.blue ( `THIS MSG TO BE REMOVED. RENAMING UUID for: ${ config.displayName } LEVEL: ${ this.LEVEL }` ) ); existingDataU[ "uuid" ] = existingDataU[ "UUID" ]; delete existingDataU[ "UUID" ]; } // NOTE: We saved the data via lower case uuid. let existingData = this.STORED_DATA_ARRAY.find( data => data[ constants.UUID ] === uuid ); if ( existingData ) { //Z this.log.info( chalk.blue ( `THIS MSG TO BE REMOVED. Found existing data for: ${ this.displayName }` ) ); if ( settings.cmd4Dbg ) this.log.debug(`Cmd4Accessory: found existingData for ${ this.displayName }` ); if ( existingData.storedValuesPerCharacteristic ) { //Z this.log.info( chalk.blue ( `THIS MSG TO BE REMOVED. Found old storedValuesPerCharacteristic for: ${ this.displayName }` ) ); if ( settings.cmd4Dbg ) this.log.debug( `Upgrading to cmd4Storage` ); this.cmd4Storage = new Cmd4Storage( this.log, existingData.storedValuesPerCharacteristic ); this.STORED_DATA_ARRAY.push( { [ constants.UUID ]: uuid, [ constants.CMD4_STORAGE_lv ]: this.cmd4Storage } ); //this.STORED_DATA_ARRAY.remove( existingData ); removeFromArray( this.STORED_DATA_ARRAY, existingData ); } else if ( existingData.cmd4Storage ) { //Z this.log.info( chalk.blue ( `THIS MSG TO BE REMOVED. Using existing cmd4Storage for: ${ this.displayName }` ) ); if ( settings.cmd4Dbg ) this.log.debug( `Using existing cmd4Storage` ); this.cmd4Storage = new Cmd4Storage( this.log, existingData.cmd4Storage ); this.STORED_DATA_ARRAY.push( { [ constants.UUID ]: uuid, [ constants.CMD4_STORAGE_lv ]: this.cmd4Storage } ); //this.STORED_DATA_ARRAY.remove( existingData ); removeFromArray( this.STORED_DATA_ARRAY, existingData ); } else { //Z log.info( chalk.blue ( `THIS MSG TO BE REMOVED. Unexpected empty cmd4Storage for: ${ this.displayName }` ) ); this.log.warn( `Unexpected empty cmd4Storage` ); this.cmd4Storage = new Cmd4Storage( this.log ); this.STORED_DATA_ARRAY.push( { [ constants.UUID ]: uuid, [ constants.CMD4_STORAGE_lv ]: this.cmd4Storage } ); //this.STORED_DATA_ARRAY.remove( existingData ); removeFromArray( this.STORED_DATA_ARRAY, existingData ); } // Do not read stored values from config.json parseConfigShouldUseCharacteristicValues = false; } else { //Z log.info( chalk.blue ( `THIS MSG TO BE REMOVED. Creating new cmd4Storage for: ${ this.displayName }` ) ); if ( settings.cmd4Dbg ) this.log.debug(`Cmd4Accessory: creating new cmd4Storage for ${ this.displayName }` ); // Instead of local variables for every characteristic, create an array to // hold values for all characteristics based on the size of all possible // characteristics. Placing them in .config will make them be cached over // restarts. this.cmd4Storage = new Cmd4Storage( this.log ); this.STORED_DATA_ARRAY.push( { [ constants.UUID ]: uuid, [ constants.CMD4_STORAGE_lv ]: this.cmd4Storage } ); } // Add the global constants to the listOfConstants if ( this.parentInfo && this.parentInfo.globalConstants != null ) { this.processConstants( this.parentInfo.globalConstants ); // Since linked accessories get processed first, The parentInfo they // get is actually "this" and we need for the linked accessory to // process the constants first in order to see them. i.e. ${IP} this.globalConstants = this.parentInfo.globalConstants; } // Direct if polling should be set or false. // You cannot copy polling from the parent because you would be copying the array // of polled characteristics that the child does not have, or turning on polling // for linked accessories too. //this.polling = false; // Init the Global Fakegato service once ! if ( FakeGatoHistoryService == null ) FakeGatoHistoryService = require( "fakegato-history" )( api ); // Get the supplied values from the accessory config. this.parseConfig( this.config, parseConfigShouldUseCharacteristicValues ); // Update the accessories namespace for stored variables // like timeout, stateChangeResponseTime ... As it may require // changes from parseConfig. this.hV.update( this ); // Add any required characteristics of a device that are missing from // a users config.json file. this.addRequiredCharacteristicStoredValues( ); // The accessory cannot have the same UUID as any other checkAccessoryForDuplicateUUID( this, this.uuid ); // The default response time is in seconds if ( ! this.stateChangeResponseTime ) this.stateChangeResponseTime = CMD4_DEVICE_TYPE_ENUM.properties[ this.typeIndex ].devicesStateChangeDefaultTime; // Check the polling config for characteristics that may be set there // and not in the config.json. this.checkPollingConfigForUnsetCharacteristics( this.polling ); // Convert the accessoriesConfig ( if any ) to an array of Cmd4Accessory if ( this.accessoriesConfig && this.CMD4 == constants.PLATFORM && this.LEVEL == 0 ) { log.info( `Creating accessories for: ${ this.displayName }` ); // Let me explain. // Level 0 are standalone or platform. // Level 1 is linked. // Added accessories are on the same level as linked, // but they are not linkedTypes, just added to the platform. // For Example: TelevisionSpeaker. let savedLevel = this.LEVEL; this.LEVEL = 1; // will be incremented to 2. this.accessories = this.accessoryTypeConfigToCmd4Accessories( this.accessoriesConfig, this ); this.LEVEL = savedLevel; } // Convert the linkedTypes ( if any ) to an array of Cmd4Accessory // Linked Accessories can be on Standalone or Platform Accessories. if ( this.linkedAccessoriesConfig && this.LEVEL == 0 ) { log.info( `Creating linked accessories for: ${ this.displayName }` ); this.linkedAccessories = this.accessoryTypeConfigToCmd4Accessories( this.linkedAccessoriesConfig, this ); } // This sets up which characteristics, if any, will be polled // This can be done for only LEVEL 0 accessories and itself if ( this.LEVEL == 0 ) { // if ( settings.cmd4Dbg ) log.debug( "CMD4=%s LEVEL=%s for %s", accessory.CMD4, accessory.LEVEL, accessory.displayName ); // The linked accessory children are at different levels of recursion, so only // allow what is posssible. if ( this.linkedAccessories && this.linkedAccessories.length > 0 ) { if ( settings.cmd4Dbg ) this.log.debug( `Setting up which characteristics will be polled for Linked Accessories of ${ this.displayName }` ); this.linkedAccessories.forEach( ( linkedAccessory ) => { if ( linkedAccessory.polling != false ) { linkedAccessory.determineCharacteristicsToPollForAccessory( linkedAccessory ); } }); } // The Television Speaker Platform Example if ( this.accessories && this.accessories.length > 0 ) { if ( settings.cmd4Dbg ) this.log.debug( `Setting up which characteristics will be polled for Added Accessories of ${ this.displayName }` ); this.accessories.forEach( ( addedAccessory ) => { if ( addedAccessory.polling ) { addedAccessory.determineCharacteristicsToPollForAccessory( addedAccessory ); } }); } if ( settings.cmd4Dbg ) this.log.debug( `Setting up which characteristics will be polled for ${ this.displayName }` ); this.determineCharacteristicsToPollForAccessory( this ); } // Create all the services for the accessory, including fakegato and polling // Only true Standalone accessories can have their services created and // polling started. Otherwise the platform will have to do this. if ( this.CMD4 == constants.STANDALONE && this.LEVEL == 0 ) { if ( settings.cmd4Dbg ) log.debug( `Creating Standalone service for: ${ this.displayName }` ); this.createServicesForStandaloneAccessoryAndItsChildren( this ) } } // Cmd4Accessory ( log, config, api, STORED_DATA_ARRAY, parentInfo ) identify( callback ) { callback( ); } getServices( ) { //if ( this.services ) //{ // if ( settings.cmd4Dbg ) this.log.debug( Fg.Red + "ZZZZ Returning:%s number of services for:%s" + Fg.Rm, this.services.length, this.displayName ); //} else { // if ( settings.cmd4Dbg ) this.log.debug( Fg.Red + "ZZZZ Returning this.services:%s for:%s" + Fg.Rm, this.services, this.displayName ); //} return this.services; } // Any required characteristic of an accessory that is not in the accessories // config will be added later by the existance of its stored value, so // find the missing characteristics and add their value s here. addRequiredCharacteristicStoredValues ( ) { // Get the properties for this accessories device type let properties = CMD4_DEVICE_TYPE_ENUM.properties[ this.typeIndex ]; // Check if required characteristics should be added, or TLV8 removed. for ( let accTypeEnumIndex = 0 ; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex++ ) { // Get the properties for this accessories device type let devProperties = CMD4_DEVICE_TYPE_ENUM.properties[ this.typeIndex ]; // See if the characteristic index is in the required characteristics of the device let requiredIndex = devProperties.requiredCharacteristics.indexOfEnum( i => i.type === accTypeEnumIndex ); let format = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].props.format; // No matter what, remove it if ( format == this.api.hap.Formats.TLV8 && this.hV.allowTLV8 == false ) { if ( this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ) != null ) { this.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, null ); this.log.warn( `****** Removing TLV8 required characteristic: ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); } continue; } // if it is required and not stored, add it if ( requiredIndex != -1 && this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ) == null ) { this.log.warn( `**** Adding required characteristic ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type } for ${ this.displayName }` ); this.log.warn( `Not defining a required characteristic can be problematic` ); // Get the default value to store let defaultValue = properties.requiredCharacteristics[ requiredIndex ].defaultValue; // If ConfiguredName was not defined, then use the Accessories Name if ( accTypeEnumIndex == CMD4_ACC_TYPE_ENUM.ConfiguredName ) defaultValue = getAccessoryName( this.config ); if ( settings.cmd4Dbg ) this.log.debug( `*****Adding default value ${ defaultValue } for: ${ this.displayName }` ); this.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, defaultValue ); } } } checkPollingConfigForUnsetCharacteristics( pollingConfig ) { if ( trueTypeOf( pollingConfig ) != Array ) return; if ( settings.cmd4Dbg ) this.log.debug( `Checking ${ this.displayName } for polling of unset characteristics.` ); pollingConfig.forEach( ( jsonPollingConfig ) => { let value; let valueToStore = null; let accTypeEnumIndex = -1; let key; for ( key in jsonPollingConfig ) { value = jsonPollingConfig[ key ]; let rcDirective = isCmd4Directive( key ); if ( rcDirective == null ) { rcDirective = isCmd4Directive( key, true ); if ( rcDirective != null ) { // warn now this.log.warn( `The config.json Cmd4 Polling Directive: ${ key } is Capitalized. It should be: ${ rcDirective.key }. In the near future this will be an error for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); // create the proper lower case value jsonPollingConfig[ rcDirective.key ] = value; // delete the upper case value delete jsonPollingConfig[ key ]; //set the key key = rcDirective.key; } } // Not finding the key is not an error as it could be a Characteristic switch ( key ) { case constants.TIMEOUT: case constants.INTERVAL: // break omitted case constants.QUEUE: { break; } case constants.QUEUETYPES: { // This whole record is not a characteristic polling entry // continue to next ( via return ) return; } case constants.CHARACTERISTIC: { //2 checkPollingOfUnsetCharacteristics valueToStore = null; let rcDirective = isAccDirective( value, false ); if ( rcDirective.accTypeEnumIndex == null ) { rcDirective = isAccDirective( value, true ); if ( rcDirective.accTypeEnumIndex == null ) throw new Error( `No such polling characteristic: "${ value }" for: "${ this.displayName }".` ); this.log.warn( `The config.json Polling characteristic: ${ value } is Capitalized it should be: ${ rcDirective.type }. In the near future this will be an Error so that Cmd4 can use homebridge-ui.\nTo remove this Warning, Please fix your config.json.` ); } accTypeEnumIndex = rcDirective.accTypeEnumIndex; // We can do this as this is a new way to do things. let storedValue = this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ); if ( storedValue == undefined ) throw new Error( `Polling for: "${ value }" requested, but characteristic is not in your config.json file for: "${ this.displayName }".` ); // This makes thinks nice down below. valueToStore = storedValue; break; } default: { // Is this still useful? accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.indexOfEnum( key ); if ( accTypeEnumIndex < 0 ) // throw new Error( `OOPS: "${ key }" not found while parsing for characteristic polling. There something wrong with your config.json file?` ); throw new Error( `OOPS: "${ key }" not found while parsing for characteristic polling of "${ this.displayName }". There something wrong with your config.json file?` ); valueToStore = value; } } } if ( accTypeEnumIndex == -1 ) throw new Error( `No characteristic found while parsing for characteristic polling of: "${ this.displayName }". There something wrong with your config.json file?` ); if ( this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ) == undefined ) { this.log.warn( `Polling for: "${ key }" requested, but characteristic` ); this.log.warn( `is not in your config.json file for: ${ this.displayName }` ); this.log.warn( `This will be an error in the future.` ); } this.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, valueToStore ); }); } createServicesForStandaloneAccessoryAndItsChildren( accessory ) { if ( settings.cmd4Dbg ) accessory.log.debug( chalk.blue( `createServicesFor${ this.CMD4 }AccessoryAndItsChildren` ) ); if ( accessory.ServiceCreated == true ) { if ( settings.cmd4Dbg ) accessory.log.debug( chalk.red( `SERVICES ALREADY CREATED FOR ${ this.displayName } ${ this.CMD4 } ${ this.LEVEL }` ) ); return; } else { accessory.ServiceCreated = true; } let properties = CMD4_DEVICE_TYPE_ENUM.properties[ accessory.typeIndex ]; // // Standalone Accessory // // Create the accessory's service accessory.service = new properties.service( accessory.name, accessory.subType ) if ( settings.cmd4Dbg ) accessory.log.debug( `Creating information service for standalone accessory: ${ accessory.displayName }` ); // Create the Standalone accessory's information service. createAccessorysInformationService( accessory ); // Create the Standalone accessory's services for all its linked children if ( accessory.linkedAccessories ) { accessory.linkedAccessories.forEach( ( linkedAccessory ) => { let properties = CMD4_DEVICE_TYPE_ENUM.properties[ linkedAccessory.typeIndex ]; // Standalone Step 4. // const hdmi1InputService = this.tvAccessory.addService( this.Service.InputSource, `hdmi1', 'HDMI 1' ); if ( settings.cmd4Dbg ) accessory.log.debug( `Standalone Step 4. linkedAccessory( ${ accessory.displayName } ).service = new Service( ${ linkedAccessory.name }, ${ linkedAccessory.subType } )` ); linkedAccessory.service = new properties.service( linkedAccessory.name, linkedAccessory.subType ) accessory.services.push( linkedAccessory.service ); // Hmmm Double Check this !! // Create Information Service //if ( settings.cmd4Dbg ) linkedAccessory.log.debug( "Creating information service for linkedAccessory:%s", linkedAccessory.displayName ); //createAccessorysInformationService( linkedAccessory ); if ( settings.cmd4Dbg ) accessory.log.debug( `Standalone Step 5. ${ accessory.displayName }.service.addLinkedService( ${ linkedAccessory.displayName }.service` ); // Standalone Step 5. // tvService.addLinkedService( hdmi1InputService ); // link to tv service accessory.service.addLinkedService( linkedAccessory.service ); linkedAccessory.addAllServiceCharacteristicsForAccessory( linkedAccessory ); // Setup the fakegato service if defined in the config.json file linkedAccessory.setupAccessoryFakeGatoService( linkedAccessory.fakegatoConfig ); // Move the information service to the top of the list linkedAccessory.services.unshift( linkedAccessory.informationService ); }); } accessory.addAllServiceCharacteristicsForAccessory( accessory ); // Setup the fakegato service if defined in the config.json file accessory.setupAccessoryFakeGatoService( accessory.fakegatoConfig ); accessory.services.push( accessory.service ); // Move the information service to the top of the list accessory.services.unshift( accessory.informationService ); } // *********************************************** // // setCachedValue: // This methos will update the cached value of a // characteristic of a accessory. // // *********************************************** setCachedValue( accTypeEnumIndex, characteristicString, value, callback ) { let self = this; if ( self.hV.statusMsg == "TRUE" ) self.log.info( chalk.blue( `Setting (Cached) ${ self.displayName } ${ characteristicString }` ) + ` ${ value }` ); else if ( settings.cmd4Dbg ) self.log.debug( `setCachedvalue accTypeEnumIndex:( ${ accTypeEnumIndex } )-"${ characteristicString }" function for: ${ self.displayName } value: ${ value }` ); // Save the cached value. // Fakegato does not need to be updated as that is done on a "Get". self.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, value ); let relatedCurrentAccTypeEnumIndex = this.getDevicesRelatedCurrentAccTypeEnumIndex( accTypeEnumIndex ); // We are currently tring to set a cached characteristics // like "Target*". // There is no way for its relatedCurrentAccTypeEnumIndex characteristic like "Current*" // to be set if cached or Polled (with the exception below). if ( relatedCurrentAccTypeEnumIndex != null ) { // We are in a "Set" but this applies to the "Get" for why we would need to // set the relatedCurrentAccTypeEnumIndex Characteristic as well. if ( self.listOfPollingCharacteristics[ relatedCurrentAccTypeEnumIndex ]) { let relatedCharacteristicString = CMD4_ACC_TYPE_ENUM.properties[ relatedCurrentAccTypeEnumIndex ].type; self.log.info( chalk.blue( `Also Setting (Cached) ${ self.displayName } ${ relatedCharacteristicString }` ) + ` ${ value }` ); self.cmd4Storage.setStoredValueForIndex( relatedCurrentAccTypeEnumIndex, value ); } } callback( null ); } // *********************************************** // // GetCachedValue: // This methos will return an accessories cached // characteristic value. // // *********************************************** getCachedValue( accTypeEnumIndex, characteristicString, callback ) { let self = this; let storedValue = self.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ); if ( storedValue == null || storedValue == undefined ) { self.log.warn( `getCachedValue ${ characteristicString } for: ${ self.displayName } has no cached value` ); callback( 10, null ); } if ( settings.cmd4Dbg ) self.log.debug( `getCachedValue ${ characteristicString } for: ${ self.displayName } returned (CACHED) value: ${ storedValue }` ); callback( 0, storedValue ); // Store history using fakegato if set up self.updateAccessoryAttribute( accTypeEnumIndex, storedValue ); } // Check props to see if any characteristic properties // are to be changed. For example, currentTemperature // minValue to be below zero. configHasCharacteristicProps( accTypeEnumIndex ) { if ( this.props == undefined ) return undefined; if ( ! isJSON( this.props ) ) return undefined; let characteristicProps = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].props; let type = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( accTypeEnumIndex ); let ucType = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( accTypeEnumIndex ); let definitions; if ( this.props[ type ] ) definitions = this.props[ type ]; if ( this.props[ ucType ] ) definitions = this.props[ ucType ]; if ( ! definitions ) return undefined; let rc = definitions; for ( let key in definitions ) { // warn now if ( key.charAt( 0 ) === key.charAt( 0 ).toUpperCase() ) { this.log.warn( `The property definition key: ${ key } is Capitalized. In the near future all characteristics will start with a lower case character for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); } let lcKey = lcFirst ( key ); if ( characteristicProps[ lcKey ] == undefined ) throw new Error( `props for key "${ key }" not in definition of "${ type }"` ); if ( typeof characteristicProps[ lcKey ] != typeof definitions[ lcKey ] ) throw new Error( `props for key "${ key }" type "${ typeof definitions[ key ] }" Not equal to definition of "${ typeof characteristicProps[ key ] }"` ); } return rc; } checkCharacteristicNeedsFixing( accessory, accTypeEnumIndex ) { // Hap keeps changing this where Current and Target don't match. // We fix this here. if ( accTypeEnumIndex == CMD4_ACC_TYPE_ENUM.CurrentHeatingCoolingState ) { if ( settings.cmd4Dbg ) this.log.debug( "fixing heatingCoolingState" ); accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ]. characteristic ).setProps( { maxValue: 3, validValues: [ 0, 1, 2, 3 ] }); } return; } // *********************************************** // // addAllServiceCharacteristicsForAccessory: // Method to set up all services for those characteristics in the // config.json file. // // // Explanation: // If you are wondering why this is done this way as compared to // other plugins that do the switch and a bind in their getServices // section; It took a week to figure out why the security // system was not getting updated after setting the target state. // The get currentState needs to be called after the set targetState, // but that was not enough. Something is different with their // getServices bind implementation. While everything works, for // some reason the IOS HomeKit app and even the Eve app never gets // the result of the get currentState. // I could delve further into their implementation, but this works. // It was one of many methods I tried after examining and trying // many plugins. // This method was taken from homebridge-real-fake-garage-doors by // plasticrake. // P.S - This is probably more documentation of code anywhere // in Homebridge :-) If you find it useful, send // me a like ;-) // // // Note: This code wipes out 5K of duplicate code. // by using a bound function. It appears // to work on my iMac. // // *********************************************** addAllServiceCharacteristicsForAccessory( accessory ) { if ( settings.cmd4Dbg ) accessory.log.debug( `Adding All Service Characteristics for: ${ accessory.displayName }` ); let perms = ""; // Check every possible characteristic for ( let accTypeEnumIndex = 0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex++ ) { // For "Get" or "Set" commands, we send uppercase let uCCharacteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( accTypeEnumIndex ); // If there is a stored value for this characteristic ( defined by the config file ) // Then we need to add the characteristic too let storedValue = accessory.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ); if ( storedValue != undefined ) { if ( settings.cmd4Dbg ) accessory.log.debug( "Found characteristic:%s value:%s for:%s", uCCharacteristicString, storedValue, this.displayName ); // Find out if the characteristic is not part of the service // and needs to be added. if ( ! accessory.service.testCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].characteristic ) ) { //if ( settings.cmd4Dbg ) accessory.log.debug( "Adding optional characteristic:%s for: %s", CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type, this.displayName ); if ( settings.cmd4Dbg ) accessory.log.debug( "Adding optional characteristic:%s for: %s", uCCharacteristicString, this.displayName ); accessory.service.addCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].characteristic ); } this.checkCharacteristicNeedsFixing( accessory, accTypeEnumIndex ); let props = accessory.configHasCharacteristicProps( accTypeEnumIndex ); if ( props ) { if ( settings.cmd4Dbg ) accessory.log.debug( "Overriding characteristic %s props for: %s ", CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type, this.displayName ); accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ]. characteristic ) .setProps( // props is an object of name value pairs (characteristics) props ); } // Get the permissions of characteristic ( Read/Write ... ) // Both are 100% the same. // perms = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].props.perms perms = accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ] .characteristic ).props.perms; // Comment before change // "Read and or write, we need to set the value once. // If the characteristic was optional and read only, this will add // it with the correct value. You cannot add and set a read characteristic." // // What was happening was at startup all writeable characteristics were calling // setValue and the MyAir was getting hammered. // We need to check if the characteristic is readable but not writeable. // Things this will set are like: // - Name // - CurrentTemperature // - CurrentHeatingCoolingState // - StatusFault // Homebridge V2 removes Perms.READ && Perms.WRITE if ( //perms.indexOf( this.api.hap.Perms.READ ) >= 0 && //perms.indexOf( this.api.hap.Perms.WRITE ) == -1 || perms.indexOf( this.api.hap.Perms.PAIRED_READ ) >= 0 && perms.indexOf( this.api.hap.Perms.PAIRED_WRITE ) == -1 ) { accessory.service.setCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].characteristic, this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ) ); } // Add getValue via getCachedValue funtion to service if ( accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ] .characteristic ).listeners( "get" ).length == 0 ) { // Add Read services for characterisitcs, if possible // Homebridge v2 removed Perms.READ if ( // perms.indexOf( this.api.hap.Perms.READ ) != -1 || perms.indexOf( this.api.hap.Perms.PAIRED_READ ) != -1 ) { // getCachedValue or getValue if ( ! accessory.polling || accessory.listOfPollingCharacteristics[ accTypeEnumIndex ] == undefined ) { if ( settings.cmd4Dbg ) this.log.debug( chalk.yellow( `Adding getCachedValue for ${ accessory.displayName } characteristic: ${ uCCharacteristicString } ` ) ); //Get cachedValue accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ] .characteristic ) .on( "get", accessory.getCachedValue.bind( accessory, accTypeEnumIndex, uCCharacteristicString ) ); } else { if ( settings.cmd4Dbg ) this.log.debug( chalk.yellow( `Adding priorityGetValue for ${ accessory.displayName } characteristic: ${ uCCharacteristicString }` ) ); let details = accessory.lookupAccessoryHVForPollingCharacteristic( accessory, accTypeEnumIndex ); // Set parms are accTypeEnumIndex, value, callback // Get parms are accTypeEnumIndex, callback accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ] .characteristic ) .on( "get", accessory.queue.priorityGetValue.bind( accessory, accTypeEnumIndex, uCCharacteristicString, details.timeout ) ); } } } // Add setValue function to service if ( accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ] .characteristic ).listeners( "set" ).length == 0 ) { // Add Write services for characterisitcs, if possible // Homebridge V2 removes Perms.WRITE if ( // perms.indexOf( this.api.hap.Perms.WRITE ) != -1 || perms.indexOf( this.api.hap.Perms.PAIRED_WRITE ) != -1 ) { // setCachedValue or setValue if ( ! accessory.polling || accessory.listOfPollingCharacteristics[ accTypeEnumIndex ] == undefined) { if ( settings.cmd4Dbg ) this.log.debug( chalk.yellow( `Adding setCachedValue for ${ accessory.displayName } characteristic: ${ uCCharacteristicString } ` ) ); // setCachedValue has parameters: // accTypeEnumIndex, value, callback // The first bound value though is "this" let boundSetCachedValue = accessory.setCachedValue.bind( this, accTypeEnumIndex, uCCharacteristicString ); accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ] .characteristic ).on( "set", ( value, callback ) => { boundSetCachedValue( value, callback ); }); } else { if ( settings.cmd4Dbg ) this.log.debug( chalk.yellow( `Adding prioritySetValue for ${ accessory.displayName } characteristic: ${ uCCharacteristicString } ` ) ); let details = accessory.lookupAccessoryHVForPollingCharacteristic( accessory, accTypeEnumIndex ); // Set parms are accTypeEnumIndex, value, callback // Get parms are accTypeEnumIndex, callback let boundSetValue = accessory.queue.prioritySetValue.bind( this, accTypeEnumIndex, uCCharacteristicString, details.timeout, details.stateChangeResponseTime ); accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ] .characteristic ).on( "set", ( value, callback ) => { boundSetValue( value, callback ); }); } } } } } } updateAccessoryAttribute( accTypeEnumIndex, value ) { if ( accTypeEnumIndex < 0 || accTypeEnumIndex > CMD4_ACC_TYPE_ENUM.EOL ) { this.log.error( `Internal error: updateAccessoryAttribute - accTypeEnumIndex: ${ accTypeEnumIndex } for: ${ this.displayName } not found` ); return; } this.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, value ); if ( this.loggingService ) { let firstParm, secondParm, thirdParm; let firstParmValue, secondParmValue, thirdParmValue = 0; let firstParmDirective, secondParmDirective, thirdParmDirective; switch ( this.eve ) { case constants.FAKEGATO_TYPE_ENERGY: { firstParm = this.fakegatoConfig[ constants.POWER ] || "0"; firstParmDirective = isAccDirective( firstParm, true ); firstParmValue = ( this.cmd4Storage.testStoredValueForIndex( firstParmDirective.accTypeEnumIndex ) == undefined ) ? firstParmValue : this.cmd4Storage.getStoredValueForIndex( firstParmDirective.accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Logging ${ constants.POWER }: ${ firstParmValue }` ); // Eve Energy ( Outlet service ) this.loggingService.addEntry( { [ constants.TIME ] : moment( ).unix( ), [ constants.POWER ] : firstParmValue }); break; } case constants.FAKEGATO_TYPE_ROOM: { firstParm = this.fakegatoConfig[ constants.TEMP ] || "0"; firstParmDirective = isAccDirective( firstParm, true ); secondParm = this.fakegatoConfig[ constants.HUMIDITY ] || "0"; secondParmDirective = isAccDirective( secondParm, true ); thirdParm = this.fakegatoConfig[ constants.PPM ] || "0"; thirdParmDirective = isAccDirective( thirdParm, true ); firstParmValue = ( this.cmd4Storage.testStoredValueForIndex( firstParmDirective.accTypeEnumIndex ) == undefined ) ? firstParmValue : this.cmd4Storage.getStoredValueForIndex( firstParmDirective.accTypeEnumIndex ); secondParmValue = ( this.cmd4Storage.testStoredValueForIndex( secondParmDirective.accTypeEnumIndex ) == undefined ) ? secondParmValue : this.cmd4Storage.getStoredValueForIndex( secondParmDirective.accTypeEnumIndex ); thirdParmValue = ( this.cmd4Storage.testStoredValueForIndex( thirdParmDirective.accTypeEnumIndex ) == undefined ) ? thirdParmValue : this.cmd4Storage.getStoredValueForIndex( thirdParmDirective.accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Logging ${ constants.TEMP }:${ firstParmValue } ${constants.HUMIDITY }:${ secondParmValue } ${ constants.PPM }:${ thirdParmValue }` ); // Eve Room ( TempSensor, HumiditySensor and AirQuality Services ) this.loggingService.addEntry( { [ constants.TIME ] : moment( ).unix( ), [ constants.TEMP ] : firstParmValue, [ constants.HUMIDITY ] : secondParmValue, [ constants.PPM ] : thirdParmValue }); break; } case constants.FAKEGATO_TYPE_WEATHER: { firstParm = this.fakegatoConfig[ constants.TEMP ] || "0"; firstParmDirective = isAccDirective( firstParm, true ); secondParm = this.fakegatoConfig[ constants.PRESSURE ] || "0"; secondParmDirective = isAccDirective( secondParm, true ); thirdParm = this.fakegatoConfig[ constants.HUMIDITY ] || "0"; thirdParmDirective = isAccDirective( thirdParm, true ); firstParmValue = ( this.cmd4Storage.testStoredValueForIndex( firstParmDirective.accTypeEnumIndex ) == undefined ) ? firstParmValue : this.cmd4Storage.getStoredValueForIndex( firstParmDirective.accTypeEnumIndex ); secondParmValue = ( this.cmd4Storage.testStoredValueForIndex( secondParmDirective.accTypeEnumIndex ) == undefined ) ? secondParmValue : this.cmd4Storage.getStoredValueForIndex( secondParmDirective.accTypeEnumIndex ); thirdParmValue = ( this.cmd4Storage.testStoredValueForIndex( thirdParmDirective.accTypeEnumIndex ) == undefined ) ? thirdParmValue : this.cmd4Storage.getStoredValueForIndex( thirdParmDirective.accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Logging ${ constants.TEMP }: ${ firstParmValue } ${ constants.PRESSURE }: ${ secondParmValue } ${ constants.HUMIDITY }: ${ thirdParmValue }` ); // Eve Weather ( TempSensor Service ) this.loggingService.addEntry( { [ constants.TIME ] : moment( ).unix( ), [ constants.TEMP ] : firstParmValue, [ constants.PRESSURE ] : secondParmValue, [ constants.HUMIDITY ] : thirdParmValue }); break; } case constants.FAKEGATO_TYPE_DOOR: { firstParm = this.fakegatoConfig[ constants.STATUS ] || "0"; firstParmDirective = isAccDirective( firstParm, true ); firstParmValue = ( this.cmd4Storage.testStoredValueForIndex( firstParmDirective.accTypeEnumIndex ) == undefined ) ? firstParmValue : this.cmd4Storage.getStoredValueForIndex( firstParmDirective.accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Logging ${ constants.STATUS } status: ${ firstParmValue }` ); this.loggingService.addEntry( { [ constants.TIME ] : moment( ).unix( ), [ constants.STATUS ] : firstParmValue }); break; } case constants.FAKEGATO_TYPE_MOTION: { firstParm = this.fakegatoConfig[ constants.STATUS ] || "0"; firstParmDirective = isAccDirective( firstParm, true ); firstParmValue = ( this.cmd4Storage.testStoredValueForIndex( firstParmDirective.accTypeEnumIndex ) == undefined ) ? firstParmValue : this.cmd4Storage.getStoredValueForIndex( firstParmDirective.accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Logging ${ constants.STATUS }: ${ firstParmValue }` ); this.loggingService.addEntry( { [ constants.TIME ] : moment( ).unix( ), [ constants.STATUS ] : firstParmValue }); break; } case constants.FAKEGATO_TYPE_THERMO: { firstParm = this.fakegatoConfig[ constants.CURRENTTEMP ] || "0"; firstParmDirective = isAccDirective( firstParm, true ); secondParm = this.fakegatoConfig[ constants.SETTEMP ] || "0"; secondParmDirective = isAccDirective( secondParm, true ); thirdParm = this.fakegatoConfig[ constants.VALVEPOSITION ] || "0"; thirdParmDirective = isAccDirective( thirdParm, true ); firstParmValue = ( this.cmd4Storage.testStoredValueForIndex( firstParmDirective.accTypeEnumIndex ) == undefined ) ? firstParmValue : this.cmd4Storage.getStoredValueForIndex( firstParmDirective.accTypeEnumIndex ); secondParmValue = ( this.cmd4Storage.testStoredValueForIndex( secondParmDirective.accTypeEnumIndex ) == undefined ) ? secondParmValue : this.cmd4Storage.getStoredValueForIndex( secondParmDirective.accTypeEnumIndex ); thirdParmValue = ( this.cmd4Storage.testStoredValueForIndex( thirdParmDirective.accTypeEnumIndex ) == undefined ) ? thirdParmValue : this.cmd4Storage.getStoredValueForIndex( thirdParmDirective.accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Logging ${ constants.CURRENTTEMP }: ${ firstParmValue } ${ constants.SETTEMP }:${ secondParmValue } ${constants.VALVEPOSITION }:${ thirdParmValue } ` ); // Eve Thermo ( Thermostat service ) this.loggingService.addEntry( { [ constants.TIME ] : moment( ).unix( ), [ constants.CURRENTTEMP ] : firstParmValue, [ constants.SETTEMP ] : secondParmValue, [ constants.VALVEPOSITION ] : thirdParmValue }); break; } case constants.FAKEGATO_TYPE_AQUA: { firstParm = this.fakegatoConfig[ constants.STATUS ] || "0"; firstParmDirective = isAccDirective( firstParm, true ); secondParm = this.fakegatoConfig[ constants.WATERAMOUNT ] || "0"; secondParmDirective = isAccDirective( secondParm, true ); firstParmValue = ( this.cmd4Storage.testStoredValueForIndex( firstParmDirective.accTypeEnumIndex ) == undefined ) ? firstParmValue : this.cmd4Storage.getStoredValueForIndex( firstParmDirective.accTypeEnumIndex ); secondParmValue = ( this.cmd4Storage.testStoredValueForIndex( secondParmDirective.accTypeEnumIndex ) == undefined ) ? secondParmValue : this.cmd4Storage.getStoredValueForIndex( secondParmDirective.accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Logging ${ constants.STATUS }: ${ firstParmValue } ${ constants.WATERAMOUNT }: ${ secondParmValue }` ); // Eve Aqua ( Valve service set to Irrigation Type ) this.LoggingService.addEntry( { [ constants.TIME ] : moment( ).unix( ), [ constants.STATUS ] : firstParmValue, [ constants.WATERAMOUNT ] : secondParmValue }); break; } } } } setupAccessoryFakeGatoService( fakegatoConfig ) { if ( fakegatoConfig == undefined ) return; for ( let key in fakegatoConfig ) { let value = fakegatoConfig[ key ]; let rcDirective = isCmd4Directive( key, false ); if ( rcDirective == null ) { rcDirective = isCmd4Directive( key, true ); if ( rcDirective != null ) { // warn now this.log.warn( `The config.json FakeGato key: ${ key } is Capitalized. It should be: ${ rcDirective.key }. In the near future this will be an error for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); // create the proper lower case value fakegatoConfig[ rcDirective.key ] = value; // delete the upper case value fakegatoConfig[ key ].remove(); //set the key key = rcDirective.key; } } switch ( key ) { case constants.EVE: this.eve = fakegatoConfig[ key ]; switch( value ) { case constants.FAKEGATO_TYPE_ENERGY: case constants.FAKEGATO_TYPE_ROOM: case constants.FAKEGATO_TYPE_WEATHER: case constants.FAKEGATO_TYPE_DOOR: case constants.FAKEGATO_TYPE_MOTION: case constants.FAKEGATO_TYPE_THERMO: case constants.FAKEGATO_TYPE_AQUA: break; default: throw new Error( `Invalid fakegato eve type: "${ value }". It must be one of ( ${ constants.FAKEGATO_TYPE_ENERGY }, ${ constants.FAKEGATO_TYPE_ROOM }, ${ constants.FAKEGATO_TYPE_WEATHER }, ${ constants.FAKEGATO_TYPE_DOOR }, ${ constants.FAKEGATO_TYPE_MOTION }, ${ constants.FAKEGATO_TYPE_THERMO }, ${ constants.FAKEGATO_TYPE_AQUA } ). Check the Cmd4 README at: "https://github.com/simont77/fakegato-history".` ); } break; case constants.STORAGE: this.storage = fakegatoConfig[ key ]; break; case constants.STORAGEPATH: this.storagePath = fakegatoConfig[ key ]; break; case constants.KEYPATH: this.keyPath = fakegatoConfig[ key ]; break; case constants.FOLDER: this.Folder = fakegatoConfig[ key ]; break; case constants.STATUS: case constants.TEMP: case constants.SETTEMP: case constants.HUMIDITY: case constants.PPM: case constants.POWER: case constants.PRESSURE: case constants.CURRENTTEMP: case constants.VALVEPOSITION: { if ( value != "0" ) { let rcDirective = isAccDirective( value, false ); if ( rcDirective.accTypeEnumIndex == null ) { rcDirective = isAccDirective( value, true ); if ( rcDirective.accTypeEnumIndex == null ) throw new Error( `Invalid characteristic "${ value }" for fakegato to log of "${ key }".` ); this.log.warn( `The config.json FakeGato characteristic: ${ value } is Capitalized it should be: ${ rcDirective.type }. In the near future this will be an Error so that Cmd4 can use homebridge-ui.\nTo remove this Warning, Please fix your config.json.` ); } // Make sure the characteristic is being polled (Changing) so I do // not get any more tickets. if ( this.queue.isCharacteristicPolled( rcDirective.accTypeEnumIndex, this.queue, this ) == false ) throw new Error(`Characteristic: "${ value }" for fakegato to log of "${ key }" is not being polled.\nHistory can not be updated continiously.` ); } break; } default: throw new Error( `Invalid fakegato key: "${ key }" in json.config for: "${ this.displayName }".` ); } } // Optional if ( this.storage != undefined ) { if ( this.storage == constants.FS ) { this.loggingService = new FakeGatoHistoryService ( this.eve, this.platform, { [ constants.STORAGE ] : constants.FS, [ constants.PATH ] : this.storagePath } ); this.services.push( this.loggingService ); } else if ( this.storage == constants.GOOGLE_DRIVE ) { this.loggingService = new FakeGatoHistoryService ( this.eve, this.platform, { [ constants.STORAGE ] : constants.GOOGLE_DRIVE, [ constants.FOLDER ] : this.folder, [ constants.KEYPATH ] : this.keyPath } ); this.services.push( this.loggingService ); } else { this.log.warn( chalk.yellow( "WARNING" ) + `: Cmd4 Unknown accessory config.storage:{ this.storage } Expected:${ constants.FS } or ${ constants.GOOGLEDRIVE } for: ${ this.displayName }` ); } } if ( this.loggingService ) { if ( ! this.polling ) { this.log.warn( `config.storage: ${ this.storage } for: ${ this.displayName } set but polling is not enabled.` ); this.log.warn( ` History will not be updated continiously.` ); } } } validateStateCmd( state_cmd ) { if ( typeof state_cmd != "string" ) throw new Error( `No state_cmd for: "${ this.displayName }".` ); // This was messy, did not like spaces. let the user be the judge of it. return true; } // We are looking for things like Volume: , Name: , Mute: parseKeyForCharacteristics( key, value, parseConfigShouldUseCharacteristicValues ) { // fix the their scripts, fix it here. let rcDirective = isAccDirective( key, false ); if ( rcDirective.accTypeEnumIndex == null ) { rcDirective = isAccDirective( key, true ); if ( rcDirective.accTypeEnumIndex == null ) throw new Error( `OOPS: "${ key }" not found for parsing characteristics in: "${ this.displayName }".` ); this.log.warn( `The config.json characteristic key: ${ key } is Capitalized. It should be: ${ rcDirective.type }. In the near future this will be an error for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); } let characteristicString = rcDirective.type; let accTypeEnumIndex = rcDirective.accTypeEnumIndex; if ( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].deprecated == true ) { this.log.warn( `The config.json characteristic: ${ characteristicString } is deprecated. It will be ignored.\nTo remove this Warning, Please fix your config.json.` ); return; } // Do not update the stored values as it is being restored from cache if ( parseConfigShouldUseCharacteristicValues == false ) return; if ( Object.keys( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].validValues ).length > 0 ) { // Even if outputConsts is not set, just in case, transpose it anyway. value = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, value ) ; } // Return the appropriate type, by seeing what it is defined as in Homebridge, let properValue = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); if ( properValue == undefined ) { // If the value is not convertable, just return it. this.log.warn( `parseKeyForCharacterisitcs: ${ this.displayName } ` + chalk.red( `Cannot convert value: ${ value } to ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].props.format } for ${ characteristicString }` ) ); return; } //this.log.debug("Setting %s to %s", characteristicString, properValue ); this.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, properValue ); } processRequires( requiresArray ) { if ( ! Array.isArray ( requiresArray ) ) throw new Error( `requires must be an array of { "require": "some requires string" }` ); // Iterate over the groups of key/value constants in the array. for ( let argIndex = 0; argIndex < requiresArray.length; argIndex++ ) { let argEntry = requiresArray[ argIndex ]; if ( argEntry.require == undefined ) throw new Error( `Requires definition at index: "${ argIndex }" has no "require":"SomeRequire"` ); let required = argEntry.require; if ( typeof required != "string" ) throw new Error( `Requires definition: "${ required }" must be a string.` ); required = this.replaceConstantsInString( required ); if ( settings.cmd4Dbg ) this.log.debug( `Requiring ${ required }` ); require( required ); } } processConstants( constantsArgArray ) { if ( ! Array.isArray ( constantsArgArray ) ) throw new Error( `Constants must be an array of { "key": "\${SomeKey}", "value": "some replacement string" }` ); // Iterate over the groups of key/value constants in the array. for ( let argIndex = 0; argIndex < constantsArgArray.length; argIndex++ ) { let argEntry = constantsArgArray[ argIndex ]; if ( argEntry.key == undefined ) throw new Error( `Constant definition at index: "${ argIndex }" has no "key":"\${SomeKey}"` ); if ( argEntry.value == undefined ) throw new Error( `Constant definition at index: "${ argIndex }" has no "value":"Some replacement string` ); let keyToAdd = argEntry.key; let valueToAdd = argEntry.value; if ( ! keyToAdd.startsWith( "${" ) ) throw new Error( `Constant definition for: "${ keyToAdd }" must start with "\${" for clarity.` ); if ( ! keyToAdd.endsWith( "}" ) ) throw new Error( `Constant definition for: "${ keyToAdd }" must end with "}" for clarity.` ); // remove any leading and trailing single quotes // so that using it for replacement will be easier. valueToAdd.replace(/^'/, "") valueToAdd.replace(/'$/, "") // Do not check for duplicates. Linked accessories add the same // ${IP} for example this.listOfConstants[ keyToAdd ] = valueToAdd; } } processVariables( variablesArgArray ) { if ( ! Array.isArray ( variablesArgArray ) ) throw new Error( `Variables must be an array of { "key": "\${SomeKey}", "value": "some replacement string" }` ); // Iterate over the groups of key/value constants in the array. for ( let argIndex = 0; argIndex < variablesArgArray.length; argIndex++ ) { let argEntry = variablesArgArray[ argIndex ]; if ( argEntry.key == undefined ) throw new Error( `Variable definition at index: "${ argIndex }" has no "key":"\${SomeKey}"` ); if ( argEntry.value == undefined ) throw new Error( `Variable definition at index: "${ argIndex }" has no "value":"Some replacement string` ); let keyToAdd = argEntry.key; let valueToAdd = argEntry.value; if ( ! keyToAdd.startsWith( "${" ) ) throw new Error( `Variable definition for: "${ keyToAdd }" must start with "\${" for clarity.` ); if ( ! keyToAdd.endsWith( "}" ) ) throw new Error( `Variable definition for: "${ keyToAdd }" must end with "}" for clarity.` ); // remove any leading and trailing single quotes // so that using it for replacement will be easier. valueToAdd.replace(/^'/, "") valueToAdd.replace(/'$/, "") this.listOfVariables[ keyToAdd ] = valueToAdd; } } accessoryTypeConfigToCmd4Accessories( config, parentInfo ) { if ( ! config ) return undefined; let that = this; if ( Array.isArray ( config ) ) { let accessories = config.map( ( accessoryConfig ) => { return new Cmd4Accessory( that.log, accessoryConfig, this.api, this.STORED_DATA_ARRAY, this ) } ); // Put the accessories into their correct collection array. parentInfo.createdCmd4Accessories.push( ...accessories ); return accessories; } let accessory = new Cmd4Accessory( that.log, config, this.api, this.STORED_DATA_ARRAY, this ); // Put the accessory into its correct collection array. parentInfo.createdCmd4Accessories.push( accessory ); return [ accessory ]; } processURL( url ) { if ( typeof url != "string" ) throw new Error( `url must be a string: "${ url }".` ); this.url = this.replaceConstantsInString( url ); } replaceConstantsInString( orig ) { let finalAns = orig; for ( let key in this.listOfConstants ) { let replacementConstant = this.listOfConstants[ key ]; finalAns = finalAns.replace( key, replacementConstant ); } return finalAns; } parseConfig( config, parseConfigShouldUseCharacteristicValues ) { let cmd4Mode = null; for ( let key in config ) { let value = config[ key ]; // warn now - Cmd4 Directives let rcDirective = isCmd4Directive( key, false ); if ( rcDirective == null ) { rcDirective = isCmd4Directive( key, true ); if ( rcDirective != null ) { this.log.warn( `The config.json Cmd4 Directive: ${ key } is Capitalized. It should be: ${ rcDirective.key }. In the near future this will be an error for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); // create the proper lower case value config[ rcDirective.key ] = value; // delete the upper case value delete config[ key ]; //set the key key = rcDirective.key; } } // Not finding the key is not an error as it could be a Characteristic switch ( key ) { case constants.TYPE: { this.type = value; let rcValue = isDevDirective( value, false ); if ( rcValue.devEnumIndex == null ) { rcValue = isDevDirective( value, true ); if ( rcValue.devEnumIndex == null ) throw new Error( `Unknown device type: "${ value }" given in: "${ this.displayName }".` ); // warn now this.log.warn( `The config.json Cmd4 device type: ${ value } is lowerCase. It should be: ${ rcValue.deviceName }. In the near future this will be an error for homebridge-UI integration.\nTo remove this Warning, Please fix your config.json.` ); this.type = rcValue.deviceName; } this.typeIndex = rcValue.devEnumIndex; if ( CMD4_DEVICE_TYPE_ENUM.properties[ this.typeIndex ].deprecated == true ) throw new Error( `Error: device type: "${ this.type }" is now deprecated in Homebridge.` ); break; } case constants.SUBTYPE: this.subType = value; break; case constants.MODEL: // createAccessorysInformationService uses these // to add to existing information service, when defined this.model = value; break; case constants.MANUFACTURER: // createAccessorysInformationService uses these // to add to existing information service, when defined this.manufacturer = value; break; case constants.SERIALNUMBER: // createAccessorysInformationService uses these // to add to existing information service, when defined this.serialNumber = value; break; case constants.DISPLAYNAME: // DisplayName is not a characteristic but used as a parm when // creating the Service. This has already been parsed, but // here so that parseConfig passes. this.displayName = value; break; case constants.UUID: // For those who define there own UUID this.uuid = value; break; case constants.ACCESSORY: // For 8.0 this is no longer supported. Homebridge has deprecated // the template and only wants Platform templates which makes // it easier for Cmd4 to support homebridge-ui as well.. throw new Error( `Accessory: has been deprecated by homebridge. Only Platform confgurations are supported by Cmd4 >= 8.0 for: "${ this.displayName }".` ); case constants.CATEGORY: // For those who define there own Category // Uppercase the category to be nice. Why do I know // this will come back to bite me. this.category = this.api.hap.Categories[ String( value ).toUpperCase( ) ]; if ( ! this.category ) throw new Error( `Category specified: "${ value }" is not a valid homebridge category for: "${ this.displayName }".` ); break; case constants.PUBLISHEXTERNALLY: // The user can make the accessory be published externally. this.publishExternally = value; break; case constants.PROPS: // Allow characteristic property changes. this.props = value; break; case constants.OUTPUTCONSTANTS: // Define if we should ouput constant strings this.outputConstants = value; break; case constants.STATUSMSG: // During state change, display a message or not if ( value === true ) this.statusMsg = "TRUE"; else this.statusMsg = "FALSE"; break; case constants.QUEUE: { let queue = queueExists( value ); if ( queue == undefined ) throw new Error( `"QueueType" must be defined first for queue "${ value }" in: "${ this.displayName }"` ); this.queue = queue; break; } case constants.TIMEOUT: // Timers are in milliseconds. A low value can result in failure to get/set values this.timeout = parseInt( value, 10 ); if ( this.timeout < 500 ) { this.log.warn( `Timeout for: ${ this.displayName } is in milliseconds. A value of "${ this.timeout }" seems pretty low` ); } break; case constants.POLLING: // Do not parse it yet as characteristics must be set first. this.polling = value; break; case constants.INTERVAL: // Intervals are in seconds this.interval = parseInt( value, 10 ) * 1000; break; case constants.STATECHANGERESPONSETIME: // respnse time is in seconds this.stateChangeResponseTime = value * 1000; break; case constants.STATE_CMD_PREFIX: // Not 100% sure why this would be needed, but // added anyway since we have a suffix this.state_cmd_prefix = value; break; case constants.STATE_CMD_SUFFIX: // This gets added after any Get/Set // We replace any constants already defined this.state_cmd_suffix = value; break; case constants.STATE_CMD: // What this plugin is all about this.state_cmd = value; break; case constants.FAKEGATO: // Do not parse it yet as characteristics must be set first. this.fakegatoConfig = value; break; case constants.REQUIRES: this.processRequires( value ); break; case constants.CONSTANTS: this.log.warn( `Warning: ${ key } will soon been deprecated at the Accessory level. Please move it to where "Platform: "Cmd4" is located in your config.json.` ); this.log.warn( `This message will disappear when you have done so.` ); this.processConstants( value ); break; case constants.VARIABLES: this.processVariables( value ); break; case constants.LINKEDTYPES: if ( settings.cmd4Dbg ) this.log.debug( `parseConfig. Found linked Accessories` ); this.linkedAccessoriesConfig = value; break; case constants.ACCESSORIES: if ( settings.cmd4Dbg ) this.log.debug( `parseConfig. Found Accessories` ); this.accessoriesConfig = value; break; case constants.URL: this.processURL( value ); break; case constants.ALLOWTLV8: this.log.warn( `Warning: ${ key } will soon been deprecated at the Accessory level. Please move it to where "Platform: "Cmd4" is located.` ); this.log.warn( `This message will disappear when you have done so.` ); this.allowTLV8 = value; break; default: { this.parseKeyForCharacteristics( key, value, parseConfigShouldUseCharacteristicValues ); } } } if ( cmd4Mode != null && this.polling ) { if ( cmd4Mode == "Demo" ) throw new Error("Demo mode is achieved when there are no polling entries in your config.json"); this.log.warn( `Cmd4 has been simplified and optimized as per: https://git.io/JtMGR.` ); this.log.warn( `To remove this message, just remove "Cmd4_Mode" from your config.json` ); } // A device type must be specified if ( this.typeIndex == undefined || this.typeIndex < 0 ) throw new Error( `Unknown device type: "${ this.type }" given in: "${ this.displayName }".` ); // Create a subType to delimit services with multiple accessories of // the same type and possibly the same accessory.name. this.subType = this.subType || this.displayName; // UUID must be defined or created. this.uuid = this.uuid || getAccessoryUUID( config, this.api.hap.uuid ); // Solve some issues people have encounterred who // have had problems with shell completion which is // only available from shell expansion. // State_cmd is only required when polling is enabled. if ( this.polling ) { // throws its own exceptio if it fails this.validateStateCmd( this.state_cmd ); } else { // Added accessories like input have no polled configuristics and therefore don't // deserve this message if ( this.level != 1) this.log.info( chalk.blue( `Cmd4 is running in Demo Mode for ${ this.displayName }` ) ); } // Handle seperation of strings of state_cmd for a prefix if ( this.state_cmd_prefix ) this.state_cmd_prefix = this.state_cmd_prefix + " "; else this.state_cmd_prefix = ""; // Handle seperation of strings of state_cmd for a suffix if ( this.state_cmd_suffix ) { if ( typeof this.state_cmd_suffix != "string" ) throw new Error( `state_cmd_suffix must be a string: "${ this.state_cmd_suffix }".` ); this.state_cmd_suffix = " " + this.replaceConstantsInString( this.state_cmd_suffix ); } else { this.state_cmd_suffix = ""; } if ( this.typeIndex == CMD4_DEVICE_TYPE_ENUM.Television ) { if ( this.CMD4 == constants.PLATFORM && ( ! this.publishExternally || ! this.category ) || this.CMD4 == constants.STANDALONE ) { this.log.warn( `Televisions should be Platform Accessories with "${ constants.PUBLISHEXTERNALLY }": true, "${ constants.CATEGORY }": "TELEVISION"` ); } if ( this.CMD4 == constants.PLATFORM && ! this.publishExternally && ( numberOfTVsPerBridge += 1 ) > 1 ) { this.log.warn( `Only one unpublished TV is allowed per bridge` ); } } // Convert polling to an Array now so that it does not need to be done multiple times later. let pollingType = trueTypeOf( this.polling ); switch( pollingType ) { case null: case undefined: if ( settings.cmd4Dbg ) this.log.debug( `No polling configured.` ); return; case Boolean: if ( settings.cmd4Dbg ) this.log.debug( `Polling config is Default Polling. Nothing to check for unset polling characteristics` ); return; case String: throw new Error( `Unknown type for Polling "${ this.polling }" given in: "${ this.displayName }".` ); case Array: break; case Object: this.log.warn( `Polling config for ${ this.displayName } should be an array.` ); this.log.warn( `Converting to array, but this may be an error in the future.` ); // Convert the object to an Array and try again. this.polling = [ this.polling ]; return; default: throw new Error( `Do not know how to handle polling type of: "${ pollingType }" for: "${ this.displayName }".` ); } // We need to check for removed characteristic Strings in the config for ( let accTypeEnumIndex = 0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex ++ ) { let storedValue = this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ); if ( storedValue != null ) { // connect the accTypeEnumIndex to its characteristic string let lcCharacteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( accTypeEnumIndex ); let ucCharacteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( accTypeEnumIndex ); if ( config[ lcCharacteristicString ] != undefined || config[ ucCharacteristicString ] != undefined ) { continue; } else { // There was a previously stored characteristic, if it was not initialized this.log.warn( `Removing previously configured characteristic: ${ lcCharacteristicString }` ); this.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, null ); } } } } // HV may change with polling characteristics lookupAccessoryHVForPollingCharacteristic( accessory, accTypeEnumIndex ) { // Heirarchy is first the default let timeout = accessory.hV.timeout; let interval = accessory.hV.interval; let stateChangeResponseTime = accessory.hV.stateChangeResponseTime; // For testing purposes where there is no queue let queueName = null; if ( accessory.queue ) queueName = accessory.queue.queueName; let pollingEntry = accessory.listOfPollingCharacteristics[ accTypeEnumIndex ]; // There should only be one, if any if ( pollingEntry != undefined ) { if ( pollingEntry.timeout ) timeout = pollingEntry.timeout; if ( pollingEntry.interval ) interval = pollingEntry.interval; if ( pollingEntry.stateChangeResponseTime ) stateChangeResponseTime = pollingEntry.stateChangeResponseTime; if ( pollingEntry.queueName ) queueName = pollingEntry.queueName; } return { [ constants.TIMEOUT_lv ]: timeout, [ constants.INTERVAL_lv ]: interval, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: stateChangeResponseTime, [ constants.QUEUE_NAME_lv ]: queueName }; } getDevicesRelatedTargetAccTypeEnumIndex( accCurrentEnumIndex ) { // Get the Devices required characteristics let requiredCharacteristicsArray = CMD4_DEVICE_TYPE_ENUM.properties[ this.typeIndex ].requiredCharacteristics; if ( requiredCharacteristicsArray.length == 0 ) return null; let found = requiredCharacteristicsArray.find( entry => entry.type == accCurrentEnumIndex ); if ( found && found.relatedTargetAccTypeEnumArray.length > 0 ) return found.relatedTargetAccTypeEnumArray[ 0 ]; return null; // For Optional, the *Target* characteristic does not have to be // defined with the *Current* characteristic as *Current* is // optional, so may be *Target* } getDevicesRelatedCurrentAccTypeEnumIndex( accTargetEnumIndex ) { // Get the Devices required characteristics let requiredCharacteristicsArray = CMD4_DEVICE_TYPE_ENUM.properties[ this.typeIndex ].requiredCharacteristics; if ( requiredCharacteristicsArray.length == 0 ) return null; let found = requiredCharacteristicsArray.find( entry => entry.type == accTargetEnumIndex ); if ( found && found.relatedCurrentAccTypeEnumArray.length > 0 ) return found.relatedCurrentAccTypeEnumArray[ 0 ]; return null; } determineCharacteristicsToPollForAccessory( accessory ) { // Get the values based on their hierarchy. let timeout = accessory.hV.timeout; let interval = accessory.hV.interval; let stateChangeResponseTime = accessory.hV.stateChangeResponseTime; // We need to create the listOfPollingCharacteristics, even in Demo mode because // this list is also used to determine if the related characteristic should be set // which happens in the Demo mode without polling. if ( typeof accessory.polling == "object" ) { if ( settings.cmd4Dbg ) this.log.debug( `Characteristic polling for: ${ accessory.displayName }` ); accessory.polling.forEach( ( jsonPollingConfig ) => { // Characteristic polling is a json type // let jsonPollingConfig = accessory.polling[ jsonIndex ]; let value; let accTypeEnumIndex = -1; // All this code disappears in the next major release. for ( let key in jsonPollingConfig ) { value = jsonPollingConfig[ key ]; let rcDirective = isCmd4Directive( key ); if ( rcDirective == null ) { rcDirective = isCmd4Directive( key, true ); if ( rcDirective != null ) { this.log.warn( `The config.json Cmd4 Polling Directive: ${ key } is Capitalized. It should be: ${ rcDirective.key }. In the near future this will be an error for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); // create the proper lower case value jsonPollingConfig[ rcDirective.key ] = value; // delete the upper case value delete jsonPollingConfig[ key ]; //set the key key = rcDirective.key; } } // Not finding the key is not an error as it could be a Characteristic switch ( key ) { case constants.TIMEOUT: // Timers are in milliseconds. A low value can result in failure to get/set values timeout = parseInt( value, 10 ); if ( timeout < 500 ) this.log.warn( `Timeout for: ${ accessory.displayName } is in milliseconds. A value of: ${ timeout } seems pretty low.` ); break; case constants.INTERVAL: // Intervals are in seconds interval = parseInt( value, 10 ) * 1000; break; case constants.STATECHANGERESPONSETIME: // respnse time is in seconds stateChangeResponseTime = value * 1000; break; case constants.CHARACTERISTIC: { // The key must be a characteristic property // but first check if one has already been defined as we can only handle one at a time. if ( accTypeEnumIndex != -1 ) throw new Error( `For charateristic polling, you can only define one characteristic per array item.\nCannot add "${ key }" as "${ value }" is already defined for: ${ accessory.displayName } ${accTypeEnumIndex}` ); rcDirective = isAccDirective( value, false ); if ( rcDirective.accTypeEnumIndex == null ) { rcDirective = isAccDirective( value, true ); if ( rcDirective.accTypeEnumIndex == null ) throw new Error( `No such polling characteristic: "${ value }" for: "${ this.displayName }".` ); this.log.warn( `1. The config.json Polling characteristic: ${ value } is Capitalized it should be: ${ rcDirective.type }. In the near future this will be an Error so that Cmd4 can use homebridge-ui.\nTo remove this Warning, Please fix your config.json.` ); } accTypeEnumIndex = rcDirective.accTypeEnumIndex; // We can do this as this is a new way to do things. if ( this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ) == undefined ) throw new Error( `CCC Polling for: "${ value }" requested, but characteristic is not in your config.json file for: "${ this.displayName }".` ); break; } default: // Switching polling key but key is unknown { throw new Error( `No such polling characteristic: "${ key }" for: "${ accessory.displayName }".` ); } } } // Everything now goes through the queue if ( this.queue == null ) { this.queue = addQueue( this.log, "Q:" + this.displayName, constants.QUEUETYPE_STANDARD, constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT ); } // This has to be UC as it gets passed to the getValue cmd string let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( accTypeEnumIndex ); if ( settings.cmd4Dbg ) this.log.debug( `Setting up accessory: ${ accessory.displayName } for polling of: ${ characteristicString } timeout: ${ timeout } interval: ${ interval } queueName: "${ this.queue.queueName }"` ); if ( timeout == undefined ) throw new Error( `determineCharacteristicsToPollForAccessory Timeout is undefined ` ); let record = { [ constants.ACCESSORY_lv ]: accessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: accTypeEnumIndex, [ constants.CHARACTERISTIC_STRING_lv ]: characteristicString, [ constants.INTERVAL_lv ]: interval, [ constants.TIMEOUT_lv ]: timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: stateChangeResponseTime, [ constants.QUEUE_NAME_lv ]: this.queue.queueName }; // Used to determine missing related characteristics and // to determine if the related characteristic is enabled. this.listOfPollingCharacteristics[ accTypeEnumIndex ] = record; this.queue.addLowPriorityGetPolledQueueEntry( record.accessory, record.accTypeEnumIndex, record.characteristicString, record.interval, record.timeout ) }); } else { // Even though polling might == undefined, we need to create a list of // would be polled characteristics for "Demo" mode // This list is also used to determine if the related characteristic should be set // which happens in the Demo mode without polling. if ( this.queue == null ) { this.queue = addQueue( this.log, "Q:" + this.displayName, constants.QUEUETYPE_STANDARD, constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT ); } // Make sure the defined characteristics will be polled CMD4_DEVICE_TYPE_ENUM.properties[ accessory.typeIndex ].defaultPollingCharacteristics.forEach( defaultPollingAccTypeEnumIndex => { // This has to be UC as it gets passed to the getValue cmd string let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( defaultPollingAccTypeEnumIndex ); let record = { [ constants.ACCESSORY_lv ]: accessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: defaultPollingAccTypeEnumIndex, [ constants.CHARACTERISTIC_STRING_lv ]: characteristicString, [ constants.INTERVAL_lv ]: interval, [ constants.TIMEOUT_lv ]: timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: stateChangeResponseTime, [ constants.QUEUE_NAME_lv ]: this.queue.queueName }; // Used to determine missing related characteristics and // to determine if the related characteristic is also polled. this.listOfPollingCharacteristics[ record.accTypeEnumIndex ] = record; // Do not create the polling record or it will start polling if ( accessory.polling == true ) { if ( settings.cmd4Dbg ) this.log.debug( `Adding ${ record.accessory.displayName } ${ CMD4_ACC_TYPE_ENUM.properties[ record.accTypeEnumIndex ].type } record.timeout: ${ record.timeout } record.interval: ${ record.interval } to Polled Queue ${ record.queueName }` ); this.queue.addLowPriorityGetPolledQueueEntry( record.accessory, record.accTypeEnumIndex, record.characteristicString, record.interval, record.timeout ) } }); } for( let accTypeEnumIndex in this.listOfPollingCharacteristics ) { // Look to see if currently polled characteristics are like "Current*" and have // a related characteristic like "Target*" let relatedTargetAccTypeEnumIndex = this.getDevicesRelatedTargetAccTypeEnumIndex( accTypeEnumIndex ); if ( relatedTargetAccTypeEnumIndex != null ) { // Check that the characteristic like "Target*" is also requested to be polled // We are in a "Set" but this applies to the "Get" for why we would need to // set the relatedCurrentAccTypeEnumIndex Characteristic as well. // - isRelated must be checked, because TemperatureSensors do // not have *Target* characteristics. if ( this.listOfPollingCharacteristics[ relatedTargetAccTypeEnumIndex ] == undefined ) { let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( accTypeEnumIndex ); let relatedCharacteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( relatedTargetAccTypeEnumIndex ); this.log.warn( `Warning, With polling for "${ characteristicString }" requested, you also must do polling of "${ relatedCharacteristicString }" or things will not function properly` ); } } } } } // Compare accessory's UUID with those already created for possible duplicates function checkAccessoryForDuplicateUUID( accessory, uuid ) { // check for UUID+subtype conflict if ( settings.cmd4Dbg ) accessory.log.debug( `Checking ${ accessory.name } for Duplicate UUID: ${ accessory.uuid }` ); for ( let existingAccessory in accessory.createdCmd4Accessories ) { if ( uuid == existingAccessory.uuid ) { // This is the same check as what is in // hap-nodejs/dist/lib/Accessory.js if ( accessory.service.subtype == existingAccessory.service.subtype ) { accessory.log.error( chalk.red( `Error` ) + `: Cannot add a bridged Accessory with the same UUID as another bridged Accessory: ${ getAccessoryName( existingAccessory ) }` ); if ( accessory.name == existingAccessory.name ) accessory.log.error( chalk.red( `Duplicate accessory names can cause this issue.` ) ); throw new Error( `It is wiser to define the second accessory in a different bridge.` ); } } // Check for duplicates in Added accessories. if ( accessory.addedAccessories && accessory.LEVEL == 0 ) { accessory.accessories.forEach( ( addedAccessory ) => { checkAccessoryForDuplicateUUID( addedAccessory, uuid ); }); } // Check for duplicates in Linked accessories. if ( accessory.linkedAccessories && accessory.LEVEL == 0 ) { accessory.linkedAccessories.forEach( ( linkedAccessory ) => { checkAccessoryForDuplicateUUID( linkedAccessory, uuid ); }); } } if ( settings.cmd4Dbg ) accessory.log.debug( `No Duplicate UUID's for this Accessory - ` + chalk.green( `OK` ) + `. Using: ${ accessory.uuid }` ); } exports.Cmd4Accessory = Cmd4Accessory; ================================================ FILE: Cmd4Platform.js ================================================ 'use strict'; // Cmd4 includes seperated out for Unit testing const { getAccessoryName, getAccessoryDisplayName } = require( "./utils/getAccessoryNameFunctions" ); const { parseAddQueueTypes } = require( "./Cmd4PriorityPollingQueue" ); let Logger = require( "./utils/Logger" ); let getAccessoryUUID = require( "./utils/getAccessoryUUID" ); let lcFirst = require( "./utils/lcFirst" ); let isNumeric = require( "./utils/isNumeric" ); let trueTypeOf = require( "./utils/trueTypeOf" ); // Hierarchy variables let HV = require( "./utils/HV" ); let createAccessorysInformationService = require( "./utils/createAccessorysInformationService" ); // Pretty Colors var chalk = require( "chalk" ); // These would already be initialized by index.js let CMD4_CHAR_TYPE_ENUMS = require( "./lib/CMD4_CHAR_TYPE_ENUMS" ).CMD4_CHAR_TYPE_ENUMS; let CMD4_DEVICE_TYPE_ENUM = require( "./lib/CMD4_DEVICE_TYPE_ENUM" ).CMD4_DEVICE_TYPE_ENUM; let CMD4_ACC_TYPE_ENUM = require( "./lib/CMD4_ACC_TYPE_ENUM" ).CMD4_ACC_TYPE_ENUM; let CMD4_FORMAT_TYPE_ENUM = CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM; let CMD4_UNITS_TYPE_ENUM = CMD4_CHAR_TYPE_ENUMS.CMD4_UNIT_TYPE_ENUM; let CMD4_PERMS_TYPE_ENUM = CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM; // The Cmd4 Classes const Cmd4Accessory = require( "./Cmd4Accessory" ).Cmd4Accessory; // Settings, Globals and Constants let settings = require( "./cmd4Settings" ); const constants = require( "./cmd4Constants" ); // Platform definition class Cmd4Platform { constructor( log, config, api ) { // Unit testing passes their own logger with debug enabled/disabled // replace with ours if ( typeof log.setOutputEnabled === "function" ) { this.log = log; // Carry the debug flag from the platform settings.cmd4Dbg = log.debugEnabled; } else { // By using our own Logger, we don't trigger others this.log = new Logger( ); if ( config[ constants.DEBUG ] == true || config[ "Debug" ] == true || process.env.DEBUG == settings.PLATFORM_NAME ) { settings.cmd4Dbg = true; } } this.log.setDebugEnabled( settings.cmd4Dbg ); if ( settings.cmd4Dbg ) this.log.debug( chalk.blue( `Class Cmd4Platform` ) ); if ( config === undefined ) return; this.config = config; this.api = api; this.Service = this.api.hap.Service; // Pass along the trigger when creating the Cmd4Accessory. // Note: The LEVEL starts at -1 as the first one gets incremented to Zero. // // LEVEL 0 Accessories are Platform or Standalone Accessories. // LEVEL 1 Accessories are linked accessories. // LEVEL 2 Accessories are added Platform accessories coerced to // level 2 as a distinction. i.e. TelevisionSpeaker. this.CMD4 = constants.PLATFORM; this.LEVEL = -1; this.toBeRestoredPlatforms = [ ]; this.createdCmd4Accessories = [ ]; this.createdCmd4Platforms = [ ]; this.globalConstants = null; this.services = [ ]; // These would be queues of Characteristics to be polled or get/set via IOS. settings.listOfCreatedPriorityQueues = { }; // Track the polling timers only so that unit testing can cancel them. this.pollingTimers = [ ]; // Create the hierarhy variables this.hV = new HV(); this.parseConfigForCmd4Directives( this.config ); // Update the namespace for stored variables // like timeout, stateChangeResponseTime ... As it may require // changes from parseConfig. this.hV.update( this ); this.processNewCharacteristicDefinitions( ); // didFinishLaunching is only called after the // registerPlatform completes. api.on( "didFinishLaunching", ( ) => { this.log.info( chalk.blue( "Cmd4Platform didFinishLaunching" ) ); this.discoverDevices( this.log ); // Any accessory not reachable must have been removed, find them this.toBeRestoredPlatforms.forEach( ( accessory ) => { if ( ! accessory.reachable ) this.removeAccessory( accessory ); }); // Let the Polling Begin this.startPolling(); }); } // Platforms do not use getServices. Good to know. //getServices( ) //{ // return this.services; //} // As Per HomeBridge: // 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. // // We do not handle restoring cached accessories ( Yet? ). Remove them // as we regenerate everything. configureAccessory( platformAccessory ) { if ( platformAccessory ) { if ( settings.cmd4Dbg ) this.log.debug( `Found cached accessory: ${ platformAccessory.displayName }` ); this.toBeRestoredPlatforms.push( platformAccessory ); } } removeAccessory( platformAccessory ) { if ( ! platformAccessory ) return; this.log.info( `Accessory ${ platformAccessory.displayName } will be removed.` ); this.api.unregisterPlatformAccessories( settings.PLUGIN_NAME, settings.PLATFORM_NAME, [ platformAccessory ] ); } // Hmmmm does not happen //configurationRequestHandler( context, request, callback ) //{ // this.log( chalk.red( `In ConfigRequestHandler context: ${ context }` ) ); // switch( context.step ) // { // case 5: // this.log( chalk.red( `Asked to remove` ) ); // break; // } //} // Only parse those CMD4 directives we care about parseConfigForCmd4Directives( config ) { for ( let key in config ) { let lcKey = lcFirst( key ); if ( key == "UUID" ) { lcKey = "uuid"; } else { // warn now if ( key.charAt( 0 ) === key.charAt( 0 ).toUpperCase( ) ) { this.log.warn( `The config.json Platform key: ${ key } is Capitalized. All keys in the near future will ALWAYS start with a lower case character for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); } } let value = config[ key ]; switch ( lcKey ) { case constants.TIMEOUT: // Timers are in milliseconds. A low value can result in // failure to get/set values this.timeout = parseInt( value, 10 ); if ( this.timeout < 500 ) this.log.warn( `Default Timeout is in milliseconds. A value of "${ this.timeout }" seems pretty low.` ); break; case constants.INTERVAL: // Intervals are in seconds this.interval = parseInt( value, 10 ) * 1000; break; case constants.STATECHANGERESPONSETIME: // respnse time is in seconds this.stateChangeResponseTime = value * 1000; break; case constants.STATE_CMD_PREFIX: // Not 100% sure why this would be needed, but // added anyway since we have a suffix this.state_cmd_prefix = value; break; case constants.STATE_CMD_SUFFIX: // This gets added after any Get/Set this.state_cmd_suffix = value; break; case constants.STATE_CMD: // What this plugin is all about this.state_cmd = value; break; case constants.OUTPUTCONSTANTS: this.outputConstants = value; break; case constants.STATUSMSG: if ( value === false ) this.statusMsg = "FALSE"; break; case constants.QUEUETYPES: parseAddQueueTypes( this.log, value ); break; case constants.DEFINITIONS: this.definitions = value; break; case constants.CONSTANTS: // Save the constants defined globally so the accessory // can parse it. this.globalConstants = value; break case constants.PLATFORM: // Noop break; case constants.ACCESSORIES: // Noop break; default: // This cannot be because all the Cmd4Accessory directives // for Standalone would have to be added. //this.log.error( chalk.red( `Error: Unknown Cmd4 Platform config option: "${ key }"` ) ); //process.exit( 448 ) ; } } } // The purpose here is not to duplicate what is in homebridge. Just to // do a little checking to make sure that what is defined won't cause // Cmd4 to balk. It is okay if homebridge does though ;-) processNewCharacteristicDefinitions( ) { if ( this.definitions == undefined ) return; if ( trueTypeOf( this.definitions ) != Array ) throw new Error( `${ constants.DEFINITIONS } is not a array.` ); this.definitions.forEach( ( definition, definitionIndex ) => { if ( settings.cmd4Dbg ) this.log.debug( `Processing definition index: ${ definitionIndex }` ); if ( trueTypeOf( definition.type ) != String ) throw new Error( `definition.type at index: ${ definitionIndex } is not a String.` ); if ( trueTypeOf( definition.description ) != String ) throw new Error( `definition.description at index: ${ definitionIndex } is not a String.` ); if ( trueTypeOf( definition.props ) != Object ) throw new Error( `definition.props at index: ${ definitionIndex } is not an Object.` ); if ( trueTypeOf( definition.props.format ) != String ) throw new Error( `definition.props.format at index: ${ definitionIndex } is not a String.` ); // Need to check if format is correct let formatIndex = CMD4_FORMAT_TYPE_ENUM.properties.indexOfEnum( i => i.type === definition.props.format ); if ( formatIndex < 0 ) throw new Error( `definition.props.format at index: ${ definitionIndex } is not a valid format.` ); if ( definition.props.units ) { if ( trueTypeOf( definition.props.units ) != String ) throw new Error( `definition.props.units at index: ${ definitionIndex } is not a String.` ); // Need to check if units is correct let unitsIndex = CMD4_UNITS_TYPE_ENUM.properties.indexOfEnum( i => i.type === definition.props.units ); if ( unitsIndex < 0 ) throw new Error( `definition.props.units at index: ${ definitionIndex } is not a valid unit.` ); } if ( definition.props.maxValue && isNumeric( definition.props.maxValue ) != true ) throw new Error( `definition.props.maxValue at index: ${ definitionIndex } is not numeric.` ); if ( definition.props.minValue && ! Number.isFinite( definition.props.minValue ) ) throw new Error( `definition.props.minValue at index: ${ definitionIndex } is not finite.` ); if ( definition.props.minStep && isNumeric( definition.props.minStep ) != true ) throw new Error( `definition.props.minStep at index: ${ definitionIndex } is not numeric.` ); if ( trueTypeOf( definition.props.perms ) != Array ) throw new Error( `definition.props.perms at index: ${ definitionIndex } is not an Array.` ); if ( definition.props.perms.length == 0 ) throw new Error( `definition.props.perms at index: ${ definitionIndex } cannot be an empty Array.` ); definition.props.perms.forEach( ( perm ) => { let permIndex = CMD4_PERMS_TYPE_ENUM.properties.indexOfEnum( i => i.type === perm ); if ( permIndex < 0 ) throw new Error( `definition.props.perms at index: ${ definitionIndex } ${ perm } is not a valid perm.` ); }); if ( definition.validValues ) { if ( trueTypeOf( definition.validValues ) != Object ) throw new Error( `definition.validValues at index: ${ definitionIndex } is not an Object.` ); } else { definition.validValues = { }; } CMD4_ACC_TYPE_ENUM.add( this.api, definition.type, definition.description, definition.props, definition.validValues ); if ( settings.cmd4Dbg ) this.log.debug( `Created definition type: "${ definition.type }".` ); }); } // These would be platform accessories with/without linked accessories discoverDevices( ) { let platform; let accessory; // loop over the config.json devices and register each one if it has not // already been registered. this.config.accessories && this.config.accessories.forEach( ( device ) => { if ( settings.cmd4Dbg ) this.log.debug( `Fetching config.json Platform accessories.` ); this.Service=this.api.hap.Service; device.name = getAccessoryName( device ); let displayName = device.displayName = getAccessoryDisplayName( device ); // 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. let uuid = getAccessoryUUID( device, this.api.hap.uuid ); // 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 // NOTE: HOMEBRIDGE EXAMPLES HAVE THIS AS UPPERCASE UUID. // lower case uuid will not be found. const existingAccessory = this.toBeRestoredPlatforms.find(accessory => accessory.UUID === uuid); if (existingAccessory) { // NOTE: HOMEBRIDGE EXAMPLES HAVE THIS AS UPPERCASE UUID. // lower case uuid will not be found. let duplicatePlatformAccessory = this.createdCmd4Platforms.find(accessory => accessory.UUID === existingAccessory.UUID); if ( duplicatePlatformAccessory ) { this.log( chalk.red( `Error duplicate platform accessory: ${ duplicatePlatformAccessory.name } uuid:${ duplicatePlatformAccessory.UUID }` ) ); // Next in for.Each object iteration return; } this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName); // if you need to update the accessory.context then you should run // `api.updatePlatformAccessories`. eg.: // existingAccessory.context.device = device; // this.api.updatePlatformAccessories( [ existingAccessory ] ); //existingAccessory.context.device = device; //this.api.updatePlatformAccessories( [ existingAccessory ] ); // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` // new ExamplePlatformAccessory( this, existingAccessory ); platform = existingAccessory; platform.Service = this.Service; // This is how we keep the device status information over restart. // Version 0 // Within STORED_DATA_ARRAY is a list of UUID identified objects // of storedValuesPerCharacteristic. // Version 1 // Within STORED_DATA_ARRAY is a list of UUID identified objects // of Cmd4Storage. // If the accessory has linked accessories or standalone accessories, // there infomation gets put in this as well. I'm pretty sure // that previosly these states were lost. // Init the STORED_DATA_ARRAY to empty [ ]. If there was never any // type to use, this is okay to. let STORED_DATA_ARRAY = [ ]; // If the saved context has our STORED_DATA_ARRAY, then use it. if ( existingAccessory.context.STORED_DATA_ARRAY ) { if ( settings.cmd4Dbg ) this.log.debug(`Cmd4Platform: Using context.STORED_DATA_ARRAY` ); STORED_DATA_ARRAY = existingAccessory.context.STORED_DATA_ARRAY; } let that = this; accessory = new Cmd4Accessory( that.log, device, this.api, STORED_DATA_ARRAY, this ); accessory.platform = platform; // Put the accessory into its correct collection array. this.createdCmd4Accessories.push( accessory ); // 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.platform.context.STORED_DATA_ARRAY = accessory.STORED_DATA_ARRAY; // Get the properties for this accessories device type let devProperties = CMD4_DEVICE_TYPE_ENUM.properties[ accessory.typeIndex ]; if ( settings.cmd4Dbg ) this.log.debug( `Step 2. ${ accessory.displayName }.service = platform.getService( Service.${ devProperties.deviceName }, ${ accessory.subType })` ); accessory.service = platform.getService( devProperties.service, accessory.name, accessory.subType ); // Determine which characteristics, if any, will be polled. This // information is also used to define which service.getValue is // used, either immediate, cached or polled. // Already done by new Cmd4Acc // accessory.determineCharacteristicsToPollOfAccessoryAndItsChildren( accessory ); // set up all services for those characteristics in the // config.json file accessory.addAllServiceCharacteristicsForAccessory( accessory ); // Create all the services for the accessory, including fakegato // true = from existing. this.createServicesForAccessoriesChildren( accessory, true ) // Since 'updateReachability()' function and the key 'reachable' are deprecated in // Homebridge_v2, We will define the key 'reachable' and set the locally read in copy // of the value to 'true' explicitly to flag that this accessory is restored from cache. existingAccessory.reachable = true; } else { // The accessory does not yet exist, so we need to create it. this.log.info('Adding new platformAccessory:', displayName); // Create the new PlatformAccessory if ( device.category == undefined ) { if ( settings.cmd4Dbg ) this.log.debug( `Step 1. platformAccessory = new platformAccessory( ${ displayName }, ${ uuid } )` ); platform = new this.api.platformAccessory( displayName, uuid ); } else { // Uppercase the category to be nice. Why do I know // this will come back to bite me. let category = this.api.hap.Categories[ String( device.category ).toUpperCase( ) ]; if ( ! category ) throw new Error( `Category specified: ${ device.category } is not a valid homebridge category.` ); if ( settings.cmd4Dbg ) this.log.debug( `Step 1. platformAccessory = new platformAccessory( ${ displayName }, ${ uuid }, ${ category } )` ); platform = new this.api.platformAccessory( displayName, uuid, category ); } platform.Service = this.Service; this.log.info( chalk.magenta( `Configuring platformAccessory: ` ) + `${ device.displayName }` ); let that = this; accessory = new Cmd4Accessory( that.log, device, this.api, [ ], this ); accessory.platform = platform // Put the accessory into its correct collection array. this.createdCmd4Accessories.push( accessory ); if ( settings.cmd4Dbg ) this.log.debug( `Created platformAccessory: ${ accessory.displayName }` ); // 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.platform.context.STORED_DATA_ARRAY = accessory.STORED_DATA_ARRAY; // Get the properties for this accessories device type let devProperties = CMD4_DEVICE_TYPE_ENUM.properties[ accessory.typeIndex ]; // MOVE OUSTSIDE // Platform Step 2. const tvService = this.tvAccessory.addService( this.Service.Television ); if ( settings.cmd4Dbg ) this.log.debug( `Step 2. ${ accessory.displayName }.service = platform.addService( this.Service.${ devProperties.deviceName }, ${ accessory.name }, ${ accessory.subType })` ); accessory.service = platform.addService( new devProperties.service( accessory.name, accessory.subType ) ); // Create all the services for the accessory, including fakegato // false = not from existing. this.createServicesForAccessoriesChildren( accessory, false ) // Step 6. this.api.publishExternalAccessories( PLUGIN_NAME, [ this.tvAccessory ] ); if ( accessory.publishExternally ) { if ( settings.cmd4Dbg ) this.log.debug( `Step 6. publishExternalAccessories( ${ settings.PLUGIN_NAME }, [ ${accessory.displayName } ] )` ); this.api.publishExternalAccessories( settings.PLUGIN_NAME, [ platform ] ); } else { if ( settings.cmd4Dbg ) this.log.debug( `Step 6. registerPlatformAccessories( ${ settings.PLUGIN_NAME }, ${ settings.PLATFORM_NAME }, [ ${ accessory.displayName } ] ) `); this.api.registerPlatformAccessories( settings.PLUGIN_NAME, settings.PLATFORM_NAME, [ platform ] ); } } // Just a flag to say we have processed this existing platform accessory. // @deprecated homebridge v2 //platform.updateReachability( true ); // For Unit testing only this.createdCmd4Platforms.push( platform ); }); } createServicesForAccessoriesChildren( cmd4PlatformAccessory, fromExisting ) { // Create the information Service for the platform itself // Unlike Standalone Accessories; The Platform information service is created // for us and the getService hangs off the platform, not the accessory. if ( cmd4PlatformAccessory.model ) { if ( settings.cmd4Dbg ) cmd4PlatformAccessory.log.debug( `Adding model( ${ cmd4PlatformAccessory.model } ) to information service of ${ cmd4PlatformAccessory.displayName }` ); cmd4PlatformAccessory.platform.getService( cmd4PlatformAccessory.platform.Service.AccessoryInformation ) .setCharacteristic( this.api.hap.Characteristic.Model, cmd4PlatformAccessory.model ); } if ( cmd4PlatformAccessory.manufacturer ) { if ( settings.cmd4Dbg ) cmd4PlatformAccessory.log.debug( `Adding manufacturer( ${ cmd4PlatformAccessory.manufacturer } ) to information service of ${ cmd4PlatformAccessory.displayName }` ); cmd4PlatformAccessory.platform.getService( cmd4PlatformAccessory.platform.Service.AccessoryInformation ) .setCharacteristic( this.api.hap.Characteristic.Manufacturer, cmd4PlatformAccessory.manufacturer ); } if ( cmd4PlatformAccessory.serialNumber ) { if ( settings.cmd4Dbg ) cmd4PlatformAccessory.log.debug( `Adding serial Number( ${ cmd4PlatformAccessory.serialNumber } ) to information service of ${ cmd4PlatformAccessory.displayName }` ); cmd4PlatformAccessory.platform.getService( cmd4PlatformAccessory.platform.Service.AccessoryInformation ) .setCharacteristic( this.api.hap.Characteristic.SerialNumber, cmd4PlatformAccessory.serialNumber ); } if ( cmd4PlatformAccessory.firmwareRevision ) { if ( settings.cmd4Dbg ) cmd4PlatformAccessory.log.debug( `Adding Firmware Revision( ${ cmd4PlatformAccessory.firmwareRevision } ) to information service of ${ cmd4PlatformAccessory.displayName }` ); cmd4PlatformAccessory.platform.getService( cmd4PlatformAccessory.platform.Service.AccessoryInformation ) .setCharacteristic( this.api.hap.Characteristic.FirmwareRevision, cmd4PlatformAccessory.firmwareRevision ); } // Create the service for all the accessories. i.e. Speaker Service // Step 3. // const speakerService = this.tvAccessory.addService( this.Service.TelevisionSpeaker ); cmd4PlatformAccessory.accessories && cmd4PlatformAccessory.accessories.forEach( ( addedAccessory ) => { // Set the platform of the added accessories so that the accessories can call methods // like this one. addedAccessory.platform = cmd4PlatformAccessory.platform; // Get the properties for this accessory's device type let devProperties = CMD4_DEVICE_TYPE_ENUM.properties[ addedAccessory.typeIndex ]; // Existing Accessories would have existing services if ( fromExisting == true ) { if ( settings.cmd4Dbg ) this.log.debug( `Platform (AddedAccessory-existing) Step 3, ${ addedAccessory.displayName }.service = accessory.platform.getService( Service.${ devProperties.deviceName }, ${ addedAccessory.name }, ${ addedAccessory.subType }` ); // If you have added more than one service of the same type to an accessory, you will need to get the service using the name you defined when adding it. //addedAccessory.service = addedAccessory.platform.getService( devProperties.service, addedAccessory.name, addedAccessory.subType ); addedAccessory.service = addedAccessory.platform.getService( addedAccessory.name, addedAccessory.subType ); } else { if ( settings.cmd4Dbg ) this.log.debug( `Platform (AddedAccessory-new) Step 3, ${ addedAccessory.displayName }.service = PlatformAccessory: ${ cmd4PlatformAccessory.displayName } addService( Service:${ devProperties.deviceName }, ${ addedAccessory.name }, ${ addedAccessory.subType } )` ); addedAccessory.service = cmd4PlatformAccessory.platform.addService( new devProperties.service( addedAccessory.name, addedAccessory.subType ) ); } addedAccessory.addAllServiceCharacteristicsForAccessory( addedAccessory ); // Create Information Service for the addedAccessory if ( settings.cmd4Dbg ) addedAccessory.log.debug( `Creating information service for AddedAccessory: ${ addedAccessory.displayName }` ); createAccessorysInformationService( addedAccessory ); // Setup the fakegato service if defined in the config.json file addedAccessory.setupAccessoryFakeGatoService( addedAccessory.fakegatoConfig ); // Move the information service to the top of the list addedAccessory.services.unshift( addedAccessory.informationService ); }); // Create the service for all the linked accessories. i.e. HDMI Service cmd4PlatformAccessory.linkedAccessories && cmd4PlatformAccessory.linkedAccessories.forEach( ( linkedAccessory ) => { // Set the platform of the linked accessories so that the accessories can call methods // like this one. linkedAccessory.platform = cmd4PlatformAccessory.platform; // Get the properties for this linked Accessory device type let devProperties = CMD4_DEVICE_TYPE_ENUM.properties[ linkedAccessory.typeIndex ]; // Child accessories can have linked accessories. i.e. HDMI accessory // Step 4. // const hdmi1InputService = this.tvAccessory.addService( this.Service.InputSource, `hdmi1', 'HDMI 1' ); // Existing Accessories would have existing services if ( fromExisting == true ) { if ( settings.cmd4Dbg ) this.log.debug( `Platform (LinkedAccessory-existing) Step 4. ${ linkedAccessory.displayName }.service = ${ cmd4PlatformAccessory.displayName }.getService:( ${ devProperties.deviceName }.service, ${linkedAccessory.name }, ${linkedAccessory.subType } )` ); // If you have added more than one service of the same type to an accessory, you will need to get the service using the name you defined when adding it. //linkedAccessory.service = linkedAccessory.platform.getService( devProperties.service, linkedAccessory.name, linkedAccessory.subType ); linkedAccessory.service = linkedAccessory.platform.getService( linkedAccessory.name, linkedAccessory.subType ); } else { if ( settings.cmd4Dbg ) this.log.debug( `Platform (LinkedAccessory-new) Step 4. ${ linkedAccessory.displayName }.service = ${ cmd4PlatformAccessory.displayName }.addService:( ${ devProperties.deviceName }.service, ${linkedAccessory.name }, ${linkedAccessory.subType } )` ); linkedAccessory.service = cmd4PlatformAccessory.platform.addService( new devProperties.service( linkedAccessory.name, linkedAccessory.subType ) ); } linkedAccessory.addAllServiceCharacteristicsForAccessory( linkedAccessory ); if ( fromExisting == false ) { if ( settings.cmd4Dbg ) this.log.debug( `Platform Step 5. ${ cmd4PlatformAccessory.displayName }.service.addLinkedService( ${ linkedAccessory.displayName }.service )` ); cmd4PlatformAccessory.service.addLinkedService( linkedAccessory.service ); } // Create Information Service for the linkedAccessory if ( settings.cmd4Dbg ) linkedAccessory.log.debug( `Creating information service for Linked Platform Accessory: ${ linkedAccessory.displayName }` ); createAccessorysInformationService( linkedAccessory ); // Setup the fakegato service if defined in the config.json file linkedAccessory.setupAccessoryFakeGatoService( linkedAccessory.fakegatoConfig ); // Move the information service to the top of the list linkedAccessory.services.unshift( linkedAccessory.informationService ); }); // Setup all the characteristics for the platform accessory itself cmd4PlatformAccessory.addAllServiceCharacteristicsForAccessory( cmd4PlatformAccessory ); // Setup the fakegato service for the platform accessory istelf. cmd4PlatformAccessory.setupAccessoryFakeGatoService( cmd4PlatformAccessory.fakegatoConfig ); } // The delay definitions are not meant to be changed, except for unit testing // ========================================================================== // staggeredStartDelay - These would be for just polling and to be nice to the system. // queuedStartDelay - As this is both IOS and polling, the delay only happens to // the low priority polling. startPolling( queuedStartDelay = 40000 ) { // Check for any queued characteristics if ( Object.keys( settings.listOfCreatedPriorityQueues ).length == 0 ) { if ( settings.cmd4Dbg ) this.log.debug( `No queued polling characteristics` ); return; } // Start polling of each queue of characteristics let lastIndex = Object.keys( settings.listOfCreatedPriorityQueues ).length; Object.keys( settings.listOfCreatedPriorityQueues ).forEach( ( queueName, index ) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; let queuedPollingTimer = setTimeout( ( ) => { if ( index == lastIndex -1 ) this.log.info( chalk.magenta( `*** Starting Polling` ) ); queue.startQueue( queue, ( ) => { if ( index == 0 ) this.log.info( chalk.magenta( `*** All characteristics are now being polled` ) ); }); }, queuedStartDelay ); this.pollingTimers.push( queuedPollingTimer ); }); } } exports.Cmd4Platform = Cmd4Platform; ================================================ FILE: Cmd4PriorityPollingQueue.js ================================================ 'use strict'; // 3rd Party includes const exec = require( "child_process" ).exec; // These would already be initialized by index.js let CMD4_ACC_TYPE_ENUM = require( "./lib/CMD4_ACC_TYPE_ENUM" ).CMD4_ACC_TYPE_ENUM; // Settings, Globals and Constants let settings = require( "./cmd4Settings" ); const constants = require( "./cmd4Constants" ); // Pretty Colors var chalk = require( "chalk" ); let trueTypeOf = require( "./utils/trueTypeOf" ); let lcFirst = require( "./utils/lcFirst" ); // For changing validValue Constants to Values and back again var { transposeConstantToValidValue, transposeValueToValidConstant, transposeBoolToValue } = require( "./utils/transposeCMD4Props" ); let HIGH_PRIORITY_SET = 0; let HIGH_PRIORITY_GET = 1; let LOW_PRIORITY_GET = 2; class Cmd4PriorityPollingQueue { constructor( log, queueName, queueType = constants.DEFAULT_QUEUE_TYPE, queueRetryCount = constants.DEFAULT_WORM_QUEUE_RETRY_COUNT ) { this.log = log; // This works better for Unit testing settings.cmd4Dbg = log.debugEnabled; this.queueName = queueName; this.queueType = queueType; this.queueRetryCount = queueRetryCount; this.queueStarted = false; this.highPriorityQueue = [ ]; this.lowPriorityQueue = [ ]; this.lowPriorityQueueIndex = 0 ; this.inProgressGets = 0; this.inProgressSets = 0; this.listOfRunningPolls = {}; // This is not a sanity timer. // This controls when it is safe to do a "Get" of the Aircon // after a failed condition. It does happen to fix the queue // when something is wrong, but this is not the purpose of // this timer. this.pauseTimer = null; this.lastGoodTransactionTime = Date.now( ); this.errorCountSinceLastGoodTransaction = 0; // - Not a const so it can be manipulated during unit testing this.pauseTimerTimeout = constants.DEFAULT_QUEUE_PAUSE_TIMEOUT; // The WoRm queue needs error messages to be silenced as // they are inevitable, but are handled through retries // By default non WoRm queues are allowed to echo errors if ( this.queueRetryCount == 0 || settings.debug ) this.echoE = true; else this.echoE = true; this.changeQueueType( this, queueType ); } echoRetryErrors( currentRetryCount ) { // If debug then the default is true if ( settings.cmd4Debug ) return true; // Since this is the last retry, echo the error if ( currentRetryCount == this.queueRetryCount ) return true; return false; } // This function is called by homebridge to *PUT AN ENTRY INTO THE HIGHEST PRIORITY SET QUEUE*. // We immediately return success if the device is accessible. Either way the Set is attempted // above everything else except other SetValue requests. prioritySetValue( accTypeEnumIndex, characteristicString, timeout, stateChangeResponseTime, value, homebridgeCallback ) { // this is Accessory // //if ( settings.cmd4Dbg ) this.log.debug(`prioritySetValue, asked to set: ${ characteristicString } to ${ value }`); // Save the value to cache. The set will come later // this.cmd4Storage.setStoredValueForIndex( accTypeEnumIndex, value ); if ( this.errorValue != 0 ) { if ( settings.cmd4Dbg ) this.log.debug(`prioritySetValue for ${ this.displayName }, homebridgeCallback returning error ${ this.errorValue } ${ this.errorString }`); homebridgeCallback( this.errorValue ); } else { if ( settings.cmd4Dbg ) this.log.debug(`prioritySetValue for ${ this.displayName }, homebridgeCallback returning default success 0`); homebridgeCallback( 0 ); } let newEntry = { [ constants.IS_SET_lv ]: true, [ constants.ACCESSORY_lv ]: this, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: accTypeEnumIndex, [ constants.CHARACTERISTIC_STRING_lv ]: characteristicString, [ constants.TIMEOUT_lv ]: timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: stateChangeResponseTime, [ constants.CALLBACK_lv ]: homebridgeCallback, [ constants.VALUE_lv ]: value }; // Determine where to put the entry in the queue if ( this.queue.highPriorityQueue.length == 0 ) { // No entries, then it goes on top this.queue.highPriorityQueue.push( newEntry ); } else { // Make sure that this is the latest "Set" of this entry let index = this.queue.highPriorityQueue.findIndex( ( entry ) => entry.accessory.uuid == this.uuid && entry.isSet == true && entry.accTypeEnumIndex == accTypeEnumIndex ); if ( index == -1 ) { // It doesn't exist in the queue, It needs to be placed after any "Sets". // First Determine the first "Get" let getIndex = this.queue.highPriorityQueue.findIndex( ( entry ) => entry.isSet == false ); if ( getIndex == -1 ) { // No "Get" entrys, it goes at the end after everything. this.queue.highPriorityQueue.push( newEntry ); } else { // Insert before the first "Get" entry this.queue.highPriorityQueue.splice( getIndex, 0, newEntry ); } } else { this.queue.highPriorityQueue[ index ] = newEntry; } } this.queue.processQueueFunc( HIGH_PRIORITY_SET, this.queue ); } // This function is called by homebridge to *PUT AN ENTRY INTO THE HIGHEST PRIORITY GET QUEUE*. // We immediately return with success and the last known value if the device is accessible, otherwise // the last failure error code. // The Get is attempted no matter the devices availability. This is done after every Set // request and at the bottom of the hightPrioritySetValue queue, but above any polling. priorityGetValue( accTypeEnumIndex, characteristicString, timeout, homebridgeCallback ) { // this is Accessory // if ( settings.cmd4Dbg ) this.log.debug(`priorityGetValue for ${ this.displayName }, asked to Get: ${ characteristicString }`); if ( this.errorValue != 0 ) { // if ( settings.cmd4Dbg ) this.log.debug(`priorityGetValue for ${ this.displayName }, homebridgeCallback returning error ${ this.errorValue } ${ this.errorString}`); homebridgeCallback( this.errorValue ); } else { // return the cached value let storedValue = this.cmd4Storage.getStoredValueForIndex( accTypeEnumIndex ); // if ( settings.cmd4Dbg ) this.log.debug(`priorityGetValue for ${ this.displayName }, homebridgeCallback returning storedValue: ${ storedValue }`); homebridgeCallback( 0, storedValue ); } if ( this.queue.queueType != constants.QUEUETYPE_WORM2 ) { // When the value is returned, it will update homebridge this.queue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: true, [ constants.ACCESSORY_lv ]: this, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: accTypeEnumIndex, [ constants.CHARACTERISTIC_STRING_lv ]: characteristicString, [ constants.TIMEOUT_lv ]: timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: homebridgeCallback } ); this.queue.processQueueFunc( HIGH_PRIORITY_GET, this.queue ); } } // This function is called by polling to *PUT AN ENTRY INTO THE LOW PRIORITY POLLING QUEUE*. addLowPriorityGetPolledQueueEntry( accessory, accTypeEnumIndex, characteristicString, interval, timeout ) { // These are all gets from polling accessory.queue.lowPriorityQueue.push( { [ constants.ACCESSORY_lv ]: accessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: accTypeEnumIndex, [ constants.CHARACTERISTIC_STRING_lv ]: characteristicString, [ constants.INTERVAL_lv ]: interval, [ constants.TIMEOUT_lv ]: timeout } ); } processHighPrioritySetQueue( entry ) { if ( settings.cmd4Dbg ) this.log.debug( `Processing high priority queue "Set" entry: ${ entry.accTypeEnumIndex } length: ${ this.highPriorityQueue.length }` ); this.inProgressSets ++; this.qSetValue( entry.accessory, entry.accTypeEnumIndex, entry.characteristicString, entry.timeout, entry.value, function ( error ) { let queue = entry.accessory.queue; // Save the error code - Pass or fail entry.accessory.errorValue = error; if ( error == 0 ) { // Now that the set was successful, store the value entry.accessory.cmd4Storage.setStoredValueForIndex( entry.accTypeEnumIndex, entry.value ); // Since the "Set" passed, do the stateChangeResponseTime setTimeout( ( ) => { // A set with no error means the queue is sane to do reading queue.lastGoodTransactionTime = Date.now( ); queue.errorCountSinceLastGoodTransaction = 0; // After the stateChangeResponseTime, do the related characteristic ( if any ) let relatedCurrentAccTypeEnumIndex = entry.accessory.getDevicesRelatedCurrentAccTypeEnumIndex( entry.accTypeEnumIndex ); if ( relatedCurrentAccTypeEnumIndex != null ) { let relatedCurrentCharacteristicString = CMD4_ACC_TYPE_ENUM.properties[ relatedCurrentAccTypeEnumIndex ].type; // Change the entry to a get and set queueGetIsUpdate to true // Use unshift to make it next in line entry.isSet = false; entry.accTypeEnumIndex = relatedCurrentAccTypeEnumIndex; entry.characteristicString = relatedCurrentCharacteristicString; entry.queueGetIsUpdate = true; queue.highPriorityQueue.unshift( entry ); } // The "Set" is now complete after its stateChangeResponseTime. queue.inProgressSets --; setTimeout( ( ) => { queue.processQueueFunc( HIGH_PRIORITY_GET, queue ); }, 0 ); return; }, entry.stateChangeResponseTime ); } else // setValue failed { // The "Set" is complete, even if it failed. queue.inProgressSets --; let currentRetryCount = queue.errorCountSinceLastGoodTransaction; if ( currentRetryCount >= queue.queueRetryCount ) { if ( queue.echoRetryErrors( currentRetryCount ) ) { // Counting starts from zero, i.e queueRetries = 0, so add 1 queue.log.warn( `*${ currentRetryCount + 1 }* error(s) were encountered for "${ entry.accessory.displayName }" getValue. Last error found Getting: "${ entry.characteristicString}". Perhaps you should run in debug mode to find out what the problem might be.` ); } // Convert the errorValue into an errorString entry.accessory.errorString = new Error( constants.errorString( error ) ); // This does not work - Nothing happens to HomeKit ! // queue.log.warn( `START processHighPrioritySetQueue calling updateCharacteristic errorValue: ${ entry.accessory.errorValue} errorString: ${ entry.accessory.errorString }`); // entry.accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ entry.accTypeEnumIndex ].characteristic ).updateValue( entry.accessory.errorString ); // queue.log.warn( `END processHighPrioritySetQueue calling updateCharacteristic errorValue: ${ entry.accessory.errorValue} errorString: ${ entry.accessory.errorString }`); } else { // Increment the errorCount/currentRetryCount queue.errorCountSinceLastGoodTransaction++; // Set failed. We need to keep trying queue.highPriorityQueue.push( entry ); } entry.accessory.queue.pauseQueue( entry.accessory.queue ); } // Note 1. // Do not call the callback as it was done when the "Set" entry was // created. // Note 2. // We cannot release the queue for further processing as the // statechangeResponseTime has not completed. This must be // done first or any next "Get" or "Set" would interfere // with the device }); } processHighPriorityGetQueue( entry ) { if ( settings.cmd4Dbg ) this.log.debug( `Processing high priority queue "Get" entry: ${ entry.accTypeEnumIndex } isUpdate: ${ entry.queueGetIsUpdate } length: ${ this.highPriorityQueue.length }` ); this.inProgressGets ++; this.qGetValue( entry.accessory, entry.accTypeEnumIndex, entry.characteristicString, entry.timeout, function ( error, properValue ) { let queue = entry.accessory.queue; // Save the error code - Pass or fail entry.accessory.errorValue = error; // Nothing special was done for casing on errors, so omit it. if ( error == 0 ) { // Save the new returned value entry.accessory.cmd4Storage.setStoredValueForIndex( entry.accTypeEnumIndex, properValue ); // hmmm if ( entry.queueGetIsUpdate == true ) entry.accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ entry.accTypeEnumIndex ].characteristic ).updateValue( properValue ); // A good anything, updates the lastGoodTransactionTime queue.lastGoodTransactionTime = Date.now( ); queue.errorCountSinceLastGoodTransaction = 0; } else // highPriority getValue failed { let currentRetryCount = queue.errorCountSinceLastGoodTransaction; if ( currentRetryCount >= queue.queueRetryCount ) { if ( queue.echoRetryErrors( currentRetryCount ) ) queue.log.warn( `*${ currentRetryCount + 1}* error(s) were encountered for "${ entry.accessory.displayName }" getValue. Last error found Getting: "${ entry.characteristicString}". Perhaps you should run in debug mode to find out what the problem might be.` ); // Convert the errorValue into an errorString entry.accessory.errorString = new Error( constants.errorString( error ) ); // This does not work - Nothing happens to HomeKit ! // queue.log.warn( `START processHighPriorityGetQueue calling updateCharacteristic errorValue: ${ entry.accessory.errorValue} errorString: ${ entry.accessory.errorString }`); // entry.accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ entry.accTypeEnumIndex ].characteristic ).updateValue( entry.accessory.errorString ); // queue.log.warn( `END processHighPriorityGetQueue calling updateCharacteristic errorValue: ${ entry.accessory.errorValue} errorString: ${ entry.accessory.errorString }`); } else { // Increment the errorCount/currentRetryCount queue.errorCountSinceLastGoodTransaction++; // High Priority Get failed. We keep retrying until the mod of // queueRetryCount reaches zero, which is WoRm only as it has more than 1 // default retry count. queue.highPriorityQueue.push( entry ); } entry.accessory.queue.pauseQueue( entry.accessory.queue ); } queue.inProgressGets --; setTimeout( ( ) => { queue.processQueueFunc( HIGH_PRIORITY_GET, queue ); }, 0 ); }); } // This is called from polling processEntryFromLowPriorityQueue( entry ) { if ( settings.cmd4Dbg ) this.log.debug( `Processing low priority queue entry: ${ entry.accTypeEnumIndex }` ); let queue = entry.accessory.queue; queue.inProgressGets ++; // isLowPriority is set to true, queue.qGetValue( entry.accessory, entry.accTypeEnumIndex, entry.characteristicString, entry.timeout, function ( error, properValue ) { // For the next one queue.inProgressGets --; // Save the error code - Pass or fail entry.accessory.errorValue = error; // Nothing special was done for casing on errors, so omit it. if ( error == 0 ) { // Save the new value entry.accessory.cmd4Storage.setStoredValueForIndex( entry.accTypeEnumIndex, properValue ); if ( settings.cmd4Dbg ) entry.accessory.log.debug( `processEntryFromLowPriorityQueue calling updateValue properValue: ${ properValue }`); // Update the new value in homebridge entry.accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ entry.accTypeEnumIndex ].characteristic ).updateValue( properValue ); // A good anything, updates the lastGoodTransactionTime queue.lastGoodTransactionTime = Date.now( ); queue.errorCountSinceLastGoodTransaction = 0; } else { // LowPriority getValue failed queue.errorCountSinceLastGoodTransaction++; // Convert the errorValue into an errorString entry.accessory.errorString = new Error( constants.errorString( error )); // This does not work - Nothing happens to HomeKit ! // Call updateValue with new Error so device will become unavailable // queue.log.warn( `START processEntryFromLowPriorityQueue calling updateCharacteristic errorValue: ${ entry.accessory.errorValue } errorString: ${ entry.accessory.errorString }`); // entry.accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ entry.accTypeEnumIndex ].characteristic ).updateValue( entry.accessory.errorString ); // queue.log.warn( `END processEntryFromLowPriorityQueue calling updateCharacteristic errorValue: ${ entry.accessory.errorValue } errorString: ${ entry.accessory.errorString }`); queue.pauseQueue( entry.accessory.queue ); } // Now that this one has been processed, schedule it again for next time queue.scheduleLowPriorityEntry( entry ) }); } // *********************************************** // // qGetValue: Method to call an external script // that returns an accessories status // for a given characteristic. // // The script will be passed: // Get // // Where: // - Device name is the name in your // config.json file. // - accTypeEnumIndex represents // the characteristic to get as in index into // the CMD4_ACC_TYPE_ENUM. // // *********************************************** qGetValue( accessory, accTypeEnumIndex, characteristicString, timeout, callback ) { let self = accessory; let queue = accessory.queue; let cmd = self.state_cmd_prefix + self.state_cmd + " Get '" + self.displayName + "' '" + characteristicString + "'" + self.state_cmd_suffix; // My AdvAir friends want to allow single quotes in accessory names, which // may have consequences with globbing for others. if ( self.state_cmd.match( /AdvAir.sh/ ) ) { cmd = self.state_cmd_prefix + self.state_cmd + ' Get "' + self.displayName + '" ' + "'" + characteristicString + "'" + self.state_cmd_suffix; } if ( settings.cmd4Dbg ) self.log.debug( `getValue: accTypeEnumIndex:( ${ accTypeEnumIndex } )-"${ characteristicString }" function for: ${ self.displayName } cmd: ${ cmd } timeout: ${ timeout }` ); let reply = "NxN"; // Execute command to Get a characteristics value for an accessory // exec( cmd, { timeout: timeout }, function ( error, stdout, stderr ) //let child = spawn( cmd, { shell:true } ); let child = exec( cmd, { timeout: timeout }, function ( error, stdout, stderr ) { if ( stderr ) if ( queue.echoE ) self.log.error( `getValue: ${ characteristicString } function for ${ self.displayName } streamed to stderr: ${ stderr }` ); // Handle errors when process closes if ( error ) if ( queue.echoE ) self.log.error( chalk.red( `getValue ${ characteristicString } function failed for ${ self.displayName } cmd: ${ cmd } Failed. Generated Error: ${ error }` ) ); reply = stdout; }).on('close', ( code ) => { // Was the return code successful ? if ( code != 0 ) { // Commands that time out have "null" return codes. So get the real one. if ( child.killed == true ) { if ( queue.echoE ) self.log.error( chalk.red( `getValue ${ characteristicString } function timed out ${ timeout }ms for ${ self.displayName } cmd: ${ cmd } Failed` ) ); callback( constants.ERROR_TIMER_EXPIRED ); return; } if ( queue.echoE ) self.log.error( chalk.red( `getValue ${ characteristicString } function failed for ${ self.displayName } cmd: ${ cmd } Failed. Error: ${ code }. ${ constants.DBUSY }` ) ); callback( code ); return; } if ( reply == "NxN" ) { if ( queue.echoE ) self.log.error( `getValue: nothing returned from stdout for ${ characteristicString } ${ self.displayName }. ${ constants.DBUSY }` ); callback( constants.ERROR_NO_DATA_REPLY ); return; } if ( reply == null ) { if ( queue.echoE ) self.log.error( `getValue: null returned from stdout for ${ characteristicString } ${ self.displayName }. ${ constants.DBUSY }` ); // We can call our callback though ;-) callback( constants.ERROR_NULL_REPLY ); return; } // Coerce to string for manipulation reply += ''; // Remove trailing newline or carriage return, then // Remove leading and trailing spaces, carriage returns ... let trimmedReply = reply.replace(/\n|\r$/,"").trim( ); // Theoretically not needed as this is caught below, but I wanted // to catch this before much string manipulation was done. if ( trimmedReply.toUpperCase( ) == "NULL" ) { if ( queue.echoE ) self.log.error( `getValue: "${ trimmedReply }" returned from stdout for ${ characteristicString } ${ self.displayName }. ${ constants.DBUSY }` ); callback( constants.ERROR_NULL_STRING_REPLY ); return; } // Handle beginning and ending matched single or double quotes. Previous version too heavy duty. // - Remove matched double quotes at begining and end, then // - Remove matched single quotes at beginning and end, then // - remove leading and trailing spaces. let unQuotedReply = trimmedReply.replace(/^"(.+)"$/,"$1").replace(/^'(.+)'$/,"$1").trim( ); if ( unQuotedReply == "" ) { if ( queue.echoE ) self.log.error( `getValue: ${ characteristicString } function for: ${ self.displayName } returned an empty string "${ trimmedReply }". ${ constants.DBUSY }` ); callback( constants.ERROR_EMPTY_STRING_REPLY ); return; } // The above "null" checked could possibly have quotes around it. // Now that the quotes are removed, I must check again. The // things I must do for bad data .... if ( unQuotedReply.toUpperCase( ) == "NULL" ) { if ( queue.echoE ) self.log.error( `getValue: ${ characteristicString } function for ${ self.displayName } returned the string "${ trimmedReply }". ${ constants.DBUSY }` ); callback( constants.ERROR_2ND_NULL_STRING_REPLY ); return; } let words = unQuotedReply.split( " " ).length; if ( words > 1 && CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].props.allowedWordCount == 1 ) { self.log.warn( `getValue: Warning, Retrieving ${ characteristicString }, expected only one word value for: ${ self.displayName } of: ${ trimmedReply }` ); } if ( settings.cmd4Dbg ) self.log.debug( `getValue: ${ characteristicString } function for: ${ self.displayName } returned: ${ unQuotedReply }` ); var transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, unQuotedReply ) if ( settings.cmd4Dbg && transposed != unQuotedReply ) self.log.debug( `getValue: ${ characteristicString } for: ${ self.displayName } transposed: ${ transposed }` ); // Return the appropriate type, by seeing what it is // defined as in Homebridge, let properValue = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( transposed ); if ( properValue == undefined ) { self.log.warn( `${ self.displayName } ` + chalk.red( `Cannot convert value: ${ unQuotedReply } to ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].props.format } for ${ characteristicString }` ) ); callback( constants.ERROR_NON_CONVERTABLE_REPLY ); return; } if ( settings.cmd4Dbg && properValue != transposed ) self.log.debug( `getValue: ${ characteristicString } for: ${ self.displayName } properValue: ${ properValue }` ); // Success !!!! callback( 0, properValue ); // Store history using fakegato if set up self.updateAccessoryAttribute( accTypeEnumIndex, properValue ); }); } // *********************************************** // // qSetValue: Method to call an external script // that sets an accessories status // for a given characteristic. // // // The script will be passed: // Set < Device Name > < accTypeEnumIndex > < Value > // // // Where: // - Device name is the name in your // config.json file. // - accTypeEnumIndex represents // the characteristic to get as in index into // the CMD4_ACC_TYPE_ENUM. // - Characteristic is the accTypeEnumIndex // in HAP form. // - Value is new characteristic value. // // Notes: // ( 1 ) In the special TARGET set characteristics, getValue // is called to update HomeKit. // Example: Set My_Door < TargetDoorState > 1 // calls: Get My_Door < CurrentDoorState > // // - Where he value in <> is an one of CMD4_ACC_TYPE_ENUM // *********************************************** qSetValue( accessory, accTypeEnumIndex, characteristicString, timeout, value, queueCallback ) { let self = accessory; let queue = accessory.queue; if ( self.hV.outputConstants == true ) { value = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, value ); } else { value = transposeBoolToValue( value ); } let cmd = accessory.state_cmd_prefix + accessory.state_cmd + " Set '" + accessory.displayName + "' '" + characteristicString + "' '" + value + "'" + accessory.state_cmd_suffix; // My AdvAir friends want to allow single quotes in accessory names, which // may have consequences with globbing for others. if ( accessory.state_cmd.match( /AdvAir.sh/ ) ) { cmd = accessory.state_cmd_prefix + accessory.state_cmd + ' Set "' + accessory.displayName + '" ' + "'" + characteristicString + "' '" + value + "'" + accessory.state_cmd_suffix; } if ( accessory.hV.statusMsg == "TRUE" ) self.log.info( chalk.blue( `Setting ${ self.displayName } ${ characteristicString }` ) + ` ${ value }` ); if ( settings.cmd4Dbg ) self.log.debug( `setValue: accTypeEnumIndex:( ${ accTypeEnumIndex } )-"${ characteristicString }" function for: ${ self.displayName } ${ value } cmd: ${ cmd } timeout: ${ timeout }` ); // Execute command to Set a characteristic value for an accessory let child = exec( cmd, { timeout: timeout }, function ( error, stdout, stderr ) { if ( stderr ) if ( queue.echoE ) self.log.error( `setValue: ${ characteristicString } function for ${ self.displayName } streamed to stderr: ${ stderr }` ); if ( error ) if ( queue.echoE ) self.log.error( chalk.red( `setValue ${ characteristicString } function failed for ${ self.displayName } cmd: ${ cmd } Failed. Error: ${ error.message }` ) ); }).on( "close", ( code ) => { if ( code != 0 ) { if ( child.killed == true ) { if ( queue.echoE ) self.log.error( chalk.red( `setValue ${ characteristicString } function failed for ${ self.displayName } cmd: ${ cmd } Failed. Error: ${ code } ${ constants.DBUSY }` ) ); queueCallback( constants.ERROR_TIMER_EXPIRED ); return; } queueCallback( code ); return; } queueCallback( code ); }); } // The queue is self maintaining, except for lowPriorityEntries // which if passed in, must be rescheduled as they go by their own // intervals and thus must handle the return code. processWormQueue( lastTransactionType, queue, lowPriorityEntry = null ) { // "WoRm", No matter what, only one "Set" allowed if ( queue.inProgressSets > 0 ) { // if ( settings.cmd4Dbg ) queue.log.debug(`processWormQueue queue.inProgressSets > 0 : ${queue.inProgressSets}`); // We are *NOT* processing the low prioirity queue entry return false; } // It is not a good time to do a anything, so skip it if ( queue.lastGoodTransactionTime == 0 ) { // if ( settings.cmd4Dbg ) queue.log.debug(`processWormQueue queue.lastGoodTransactionTime == 0`); // We are *NOT* processing the low prioirity queue entry return false; } if ( queue.highPriorityQueue.length > 0 ) { let nextEntry = queue.highPriorityQueue[ 0 ]; if ( nextEntry.isSet == true ) { // If already in progress, when they finish they will restart the queue // Otherwise continuing will purge the next item from the queue as it // cannot be run with an entry already in progress. if ( nextEntry.accessory.queue.inProgressSets > 0 || nextEntry.accessory.queue.inProgressGets > 0 ) { // Return as queue is busy. // Return false as we are *NOT* processing the low prioirity queue entry // if ( settings.cmd4Dbg ) queue.log.debug(`processWormQueue queue.inProgressSets> 0 ${ nextEntry.accessory.queue.inProgressSets } ${ nextEntry.accessory.queue.inProgressGets }`); return false; } queue.processHighPrioritySetQueue( queue.highPriorityQueue.shift( ) ); // Return false as we are *NOT* processing the low prioirity queue entry return false; } // This must be a "Get". Process them all. let max = queue.highPriorityQueue.length; while( queue.highPriorityQueue.length > 0 && nextEntry.isSet == false && max >= 1 ) { queue.processHighPriorityGetQueue( queue.highPriorityQueue.shift( ) ); nextEntry = queue.highPriorityQueue[ 0 ]; max--; } // Return false as we are *NOT* processing the low prioirity queue entry return false; } else if ( lastTransactionType == HIGH_PRIORITY_SET || lastTransactionType == HIGH_PRIORITY_GET ) { // Return false as we are *NOT* processing the low prioirity queue entry return false; } // This is self evident, until their are other types of Prioritys if ( lastTransactionType == LOW_PRIORITY_GET && lowPriorityEntry != null && queue.queueStarted == true ) { queue.processEntryFromLowPriorityQueue( lowPriorityEntry ); // We are processing the low priority queue entry. return true; } else { if ( lastTransactionType == LOW_PRIORITY_GET && queue.queueStarted == false ) { // Return false as we are *NOT* processing the low prioirity queue entry return false; } if ( queue.inProgressGets == 0 && queue.inProgressSets == 0 ) { // Return false as we are *NOT* processing the low prioirity queue entry return false; } else { if ( settings.cmd4Dbg ) this.log.debug( `Unhandled lastTransactionType: ${ lastTransactionType } inProgressSets: ${ queue.inProgressSets } inProgressGets: ${ queue.inProgressGets } queueStarted: ${ queue.queueStarted } lowQueueLen: ${ queue.lowPriorityQueue.length } hiQueueLen: ${ queue.highPriorityQueue.length }` ); } } } // The queue is self maintaining, except for lowPriorityEntries // which if passed in, must be rescheduled as they go by their own // intervals and thus must handle the return code. processSequentialQueue( lastTransactionType, queue, lowPriorityEntry = null ) { // Sequential, No matter what, only one transaction allowed if ( queue.inProgressSets > 0 || queue.inProgressGets > 0 ) // Return false as we are *NOT* processing the low prioirity queue entry return false; // It is not a good time to do a anything, so skip it if ( queue.lastGoodTransactionTime == 0 ) // Return false as we are *NOT* processing the low prioirity queue entry return false; if ( queue.highPriorityQueue.length > 0 ) { let nextEntry = queue.highPriorityQueue[ 0 ]; if ( nextEntry.isSet == true ) { queue.processHighPrioritySetQueue( queue.highPriorityQueue.shift( ) ); // Return false as we are *NOT* processing the low prioirity queue entry return false; } // Has to be a High Priority "Get" entry. Process just this one. queue.processHighPriorityGetQueue( queue.highPriorityQueue.shift( ) ); // Return false as we are *NOT* processing the low prioirity queue entry return false; } else if ( lastTransactionType == HIGH_PRIORITY_SET || lastTransactionType == HIGH_PRIORITY_GET ) { // Return false as we are *NOT* processing the low prioirity queue entry return false; } // This is self evident, until their are other types of Prioritys if ( lastTransactionType == LOW_PRIORITY_GET && lowPriorityEntry != null && queue.queueStarted == true ) { queue.processEntryFromLowPriorityQueue( lowPriorityEntry ); // We are processing the low priority queue entry. return true; } else { if ( lastTransactionType == LOW_PRIORITY_GET && queue.queueStarted == false ) { // Return false as we are *NOT* processing the low prioirity queue entry return false; } if ( queue.inProgressGets == 0 && queue.inProgressSets == 0 ) { // Return false as we are *NOT* processing the low prioirity queue entry return false; } else { if ( settings.cmd4Dbg ) this.log.debug( `Unhandled lastTransactionType: ${ lastTransactionType } inProgressSets: ${ queue.inProgressSets } inProgressGets: ${ queue.inProgressGets } queueStarted: ${ queue.queueStarted } lowQueueLen: ${ queue.lowPriorityQueue.length } hiQueueLen: ${ queue.highPriorityQueue.length }` ); } } } // The standard queue is just free running, except if the queue has not // been started yet. processPassThruQueue( lastTransactionType, queue, lowPriorityEntry = null ) { if ( lastTransactionType == LOW_PRIORITY_GET && lowPriorityEntry != null && queue.queueStarted == true ) { queue.processEntryFromLowPriorityQueue( lowPriorityEntry ); } if ( queue.highPriorityQueue.length > 0 ) { let nextEntry = queue.highPriorityQueue[ 0 ]; if ( nextEntry.isSet == true ) { queue.processHighPrioritySetQueue( queue.highPriorityQueue.shift( ) ); } else { queue.processHighPriorityGetQueue( queue.highPriorityQueue.shift( ) ); } } } scheduleLowPriorityEntry( entry ) { let accessory = entry.accessory; let queue = entry.accessory.queue; if ( settings.cmd4Dbg ) accessory.log.debug( `Scheduling Poll of index: ${ entry.accTypeEnumIndex } characteristic: ${ entry.characteristicString } for: ${ accessory.displayName } timeout: ${ entry.timeout } interval: ${ entry.interval }` ); // Clear polling if ( queue.listOfRunningPolls && queue.listOfRunningPolls[ accessory.displayName + entry.accTypeEnumIndex ] == undefined ) clearTimeout( queue.listOfRunningPolls[ accessory.displayName + entry.accTypeEnumIndex ] ); queue.listOfRunningPolls[ accessory.displayName + entry.accTypeEnumIndex ] = setTimeout( ( ) => { // If the queue was busy/not available, schedule the entry at a later time if ( queue.processQueueFunc( LOW_PRIORITY_GET, queue, entry ) == false ) { if ( settings.cmd4Dbg ) accessory.log.debug( `processsQueue returned false` ); queue.scheduleLowPriorityEntry( entry ); } }, entry.interval); } pauseQueue( queue ) { if ( queue.queueType == constants.QUEUETYPE_STANDARD ) return; queue.lastGoodTransactionTime = 0; if ( queue.pauseTimer == null ) { queue.pauseTimer = setTimeout( ( ) => { // So we do not trip over this again immediately queue.lastGoodTransactionTime = Date.now( ); queue.pauseTimer = null; queue.processQueueFunc( HIGH_PRIORITY_GET, queue ); }, queue.pauseTimerTimeout ); } } printQueueStats( queue ) { let line = `QUEUE "${ queue.queueName }" stats`; this.log.info( line ); this.log.info( `${ "=".repeat( line.length ) }` ); this.log.info( "No longer applicable" ); } dumpQueue( queue ) { let line = `Low Priority Queue "${ queue.queueName }"`; this.log.info( line ); this.log.info( `${ "=".repeat( line.length ) }` ); queue.lowPriorityQueue.forEach( ( entry, entryIndex ) => { this.log.info( `${ entryIndex } ${ entry.accessory.displayName } characteristic: ${ entry.characteristicString } accTypeEnumIndex: ${ entry.accTypeEnumIndex } interval: ${ entry.interval } timeout: ${ entry.timeout }` ); } ); } startQueue( queue, allDoneCallback ) { queue.lowPriorityQueueIndex = 0 ; let delay = 0; let staggeredDelays = [ 3000, 6000, 9000, 12000 ]; let staggeredDelaysLength = staggeredDelays.length; let staggeredDelayIndex = 0; let lastAccessoryUUID = "" let allDoneCount = 0; if ( settings.cmd4Dbg ) this.log.debug( `enablePolling for the first time` ); // If there is nothing in the lowPriorityQueue, we are dome. // Demo mode or Unit testing. if ( queue.lowPriorityQueue.length == 0 ) { allDoneCallback( allDoneCount ); setTimeout( ( ) => { queue.processQueueFunc( HIGH_PRIORITY_GET, queue ); }, 0 ); } else { queue.lowPriorityQueue.forEach( ( entry, entryIndex ) => { allDoneCount ++; setTimeout( ( ) => { if ( entryIndex == 0 && settings.cmd4Dbg ) { if ( queue.queueType == constants.QUEUETYPE_WORM || queue.queueType == constants.QUEUETYPE_WORM2 ) { entry.accessory.log.debug( `Started staggered kick off of ${ queue.lowPriorityQueue.length } polled characteristics for queue: "${ entry.accessory.queue.queueName }"` ); } else { entry.accessory.log.debug( `Started staggered kick off of ${ queue.lowPriorityQueue.length } polled characteristics for "${ entry.accessory.displayName }"` ); } } if ( settings.cmd4Dbg ) entry.accessory.log.debug( `Kicking off polling for: ${ entry.accessory.displayName } ${ entry.characteristicString } interval:${ entry.interval }, staggered:${ staggeredDelays[ staggeredDelayIndex ] }` ); queue.scheduleLowPriorityEntry( entry ); if ( entryIndex == queue.lowPriorityQueue.length -1 ) { if ( settings.cmd4Dbg ) { if ( queue.queueType == constants.QUEUETYPE_WORM || queue.queueType == constants.QUEUETYPE_WORM2 ) { entry.accessory.log.debug( `All characteristics are now being polled for queue: "${ queue.queueName }"` ); } else { entry.accessory.log.debug( `All characteristics are now being polled for "${ entry.accessory.displayName }"` ); } } allDoneCallback( allDoneCount ); } }, delay ); if ( staggeredDelayIndex++ >= staggeredDelaysLength ) staggeredDelayIndex = 0; if ( lastAccessoryUUID != entry.accessory.uuid ) staggeredDelayIndex = 0; lastAccessoryUUID = entry.accessory.uuid; delay += staggeredDelays[ staggeredDelayIndex ]; }); } queue.queueStarted = true; } changeQueueType( queue, queueType ) { if ( queue.queueStarted ) throw new Error( `Cannot change queueType when queue is running` ); // The WoRm queue needs error messages to be silenced as // they are inevitable, but are handled through retries // By default non WoRm queus are allowed to echo errors this.echoE = true; // Default this.processQueueFunc = this.processWormQueue; switch ( queueType ) { case constants.QUEUETYPE_SEQUENTIAL: this.processQueueFunc = this.processSequentialQueue; break; case constants.QUEUETYPE_WORM: case constants.QUEUETYPE_WORM2: this.processQueueFunc = this.processWormQueue; // When not in debug mode, do not echo errors for the WoRm queue // as errors are handled through retries. if ( ! settings.cmd4Dbg ) this.echoE = false; break; case constants.QUEUETYPE_STANDARD: // only polled entries go straight through the queue this.processQueueFunc = this.processPassThruQueue; break; case constants.QUEUETYPE_PASSTHRU: // entries go straight through the queue this.processQueueFunc = this.processPassThruQueue; break; default: this.log.error( `Error: Invalid queue type: ${ queueType }` ); } } isCharacteristicPolled( accTypeEnumIndex, queue, accessory ) { if ( queue.lowPriorityQueue.filter( entry => entry.accessory.uuid == accessory.uuid && entry.accTypeEnumIndex == accTypeEnumIndex ).length == 0 ) { return false; } return true; } } var queueExists = function( queueName ) { return settings.listOfCreatedPriorityQueues[ queueName ]; } var addQueue = function( log, queueName, queueType = constants.DEFAULT_QUEUE_TYPE, queueRetryCount = constants.DEFAULT_WORM_QUEUE_RETRY_COUNT ) { let queue = queueExists( queueName ); if ( queue != undefined ) return queue; log.debug( `Creating new Priority Polled Queue "${ queueName }" with QueueType of: "${ queueType }" retryCount: ${queueRetryCount}` ); queue = new Cmd4PriorityPollingQueue( log, queueName, queueType, queueRetryCount ); settings.listOfCreatedPriorityQueues[ queueName ] = queue; return queue; } var parseAddQueueTypes = function ( log, entrys ) { if ( trueTypeOf( entrys ) != Array ) throw new Error( `${ constants.QUEUETYPES } is not an Array of { "Queue Name": "QueueType" }. found: ${ entrys }` ); entrys.forEach( ( entry, entryIndex ) => { let queueName = null; let queueType = constants.DEFAULT_QUEUE_TYPE; let queueRetryCount = constants.DEFAULT_WORM_QUEUE_RETRY_COUNT; for ( let key in entry ) { let lcKey = lcFirst( key ); // warn now if ( key.charAt( 0 ) === key.charAt( 0 ).toUpperCase( ) ) { log.warn( `The config.json queueTypes key: ${ key } is Capitalized. All keys in the near future will ALWAYS start with a lower case character for homebridge-ui integration.\nTo remove this Warning, Please fix your config.json.` ); } let value = entry[ key ]; switch( lcKey ) { case constants.QUEUE: if ( settings.listOfCreatedPriorityQueues[ entry.queue ] ) throw new Error( `QueueName: ${ entry.queue } was added twice` ); queueName = value; break; case constants.QUEUETYPE: // Set the default Queue Retry Count based on QueueType switch ( value ) { case constants.QUEUETYPE_WORM: queueRetryCount = constants.DEFAULT_WORM_QUEUE_RETRY_COUNT; queueType = value; break; case constants.QUEUETYPE_WORM2: queueRetryCount = constants.DEFAULT_WORM_QUEUE_RETRY_COUNT; queueType = value; break; case constants.QUEUETYPE_SEQUENTIAL: queueRetryCount = constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT; queueType = value; break; case constants.QUEUETYPE_STANDARD: queueRetryCount = constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT; queueType = value; break; default: throw new Error( `QueueType: ${ entry.queueType } is not valid at index: ${ entryIndex }. Expected: ${ constants.QUEUETYPE_WORM }, ${ constants.QUEUETYPE_WORM2 } or ${ constants.QUEUETYPE_SEQUENTIAL }` ); } break; case constants.QUEUE_RETRIES: queueRetryCount = value; break; default: throw new Error( `Unknown Queue option"${ key } not provided at index ${ entryIndex }` ); } } // At least a Queue name must be defined, the rest are defaulted if ( queueName == null ) throw new Error( `"${ constants.QUEUE }" not provided at index ${ entryIndex }` ); if ( settings.cmd4Dbg ) log.debug( `calling addQueue: ${ queueName } type: ${ queueType } retryCount: ${ queueRetryCount }` ); addQueue( log, queueName, queueType, queueRetryCount ); } ); } module.exports = { addQueue, parseAddQueueTypes, queueExists, Cmd4PriorityPollingQueue } ================================================ FILE: Extras/Cmd4Scripts/CheckYourScript.sh ================================================ #!/bin/bash --noprofile --norc # Fun colour & cursor stuff TCR=$(tput cr) TCLR=$(tput clear) TBLD=$(tput bold) TNRM=$(tput sgr0) TBLK=$(tput setaf 0) TRED=$(tput setaf 1) TGRN=$(tput setaf 2) TYEL=$(tput setaf 3) TBLU=$(tput setaf 4) TMAG=$(tput setaf 5) TCYN=$(tput setaf 6) TWHT=$(tput setaf 7) # OS type for flavours of different commands (like date on OSX) case $(uname | tr '[:upper:]' '[:lower:]') in solaris*) date_cmd="date -u +%s.%N" ;; darwin*) # OSX does not have msec date_cmd="date -u +%s" ;; linux*) date_cmd="date -u +%s.%N" ;; bsd*) date_cmd="date -u +%s.%N" ;; msys*) date_cmd="date -u +%s.%N" ;; *) echo "unknown: OSTYPE:$OSTYPE" exit -1 ;; esac printf "${TCLR}" if [ "${1}" = '-h' ]; then printf "${TBLU}Usage:${TNRM}" printf "${TNRM} SHELL> cd \n" printf "${TNRM} SHELL> bash --noprofile --norc \n" printf "\n" printf "${TBLU}Syntax:${TNRM}\n" printf " ${TBLU}${0}${TNRM} 'full state command'\n" printf "${TBLU}i.e.${TNRM}\n" printf " '.homebridge/Cmd4Scripts/CheckYourScript.sh' 'bin/MyExec' 'Get' 'MyDevice' 'On'\n" printf " or\n" printf " '.homebridge/Cmd4Scripts/CheckYourScript.sh' 'bash bin/YourScript.sh' 'Get' 'MyDevice' 'On'\n" printf " or\n" printf " '.homebridge/Cmd4Scripts/CheckYourScript.sh' 'bin/YourScript.sh 'Get 'MyDevice' 'On'\n" printf "\n" printf "Note: Add the '' around the command to prevent globbing, which is not done by homebridge-cmd4\n" exit 0 fi # Processes are run from your home directory, so go there first. cd "${HOME}" printf "${TBLU}Changing to:${TNRM}'${HOME}' ${TBLU}where processes are run from${TNRM}\n" # $HOME is expanded by the shell and not scripts, so do not rely on it. unset HOME printf "${TBLU}Enviroment in shell is limited to these variables:${TNRM}\n" env printf "\n" printf "${TBLU}Command will be run from the directory: ${TNRM}${PWD}\n" output="" rc=0 if [ "$2" = 'Set' ] || [ "$3" = 'Set' ]; then printf "${TBLU}(Set) Cmd4 would execute:${TNRM} $* " printf "\n" start_time="$( $date_cmd )" output=$("$@") rc="$?" end_time="$( $date_cmd )" # The elapsed time (in microseconds) # We add 1 second as OSX does not have msec dates. # One second will make no difference, but might help a newbie. elapsed=( $(/usr/bin/bc <<<"($end_time-$start_time) * 1000 + 1000") ) printf "\n" if [ $rc = 0 ]; then printf "${TGRN}Command passed with returned code:${TNRM}'${rc}'\n" if [ "${output}" != "" ]; then printf "Output: '${output}' would be ignored\n" fi printf "$TBLU}The timeout value should be at least :${TNRM} ${elapsed} (microseconds)\n" printf "$TBLU}Multiply by 5 for safety.${TNRM}\n" else printf "${TRED}Command given did not exit with a ${TNRM}0${TRED} return code and would fail in homebridge-cmd4 rc=${TNRM}'${rc}'\n" printf "Understand that the error given is a result of running your command in a basic shell environment. Head the errors given\n" fi else printf "${TBLU}(Get) Cmd4 would execute:${TNRM} $*" printf "\n" start_time="$( $date_cmd )" output=$("$@") rc="$?" end_time="$( $date_cmd )" # The elapsed time (in microseconds) # We add 1 second as OSX does not have msec dates. elapsed=( $(/usr/bin/bc <<<"($end_time-$start_time) * 1000 + 1000") ) printf "\n" wordCount=0 if [ $rc = 0 ]; then printf "${TGRN}Command passed with returned code:${TNRM}'${rc}'\n" printf "${TBLU}Output: of command was:${TNRM}'${output}'\n" wordCount=$(IFS=' '; set -f; set -- "${output}"; echo $#) if [ "${wordCount}" != '1' ]; then printf "${TYEL}Word count of output should only be 1, not ${TNRM}'${wordCount}'\n" fi printf "$TBLU}The timeout value should be at least :${TNRM} ${elapsed} (microseconds)\n" printf "$TBLU}Multiply by 5 for safety.${TNRM}\n" else printf "${TRED}Command given did not exit with a ${TNRM}0${TRED} return code and would fail in homebridge-cmd4 rc=${TNRM}'${rc}'\n" printf "Understand that the error given is a result of running your command in a basic shell environment. Head the errors given\n" fi fi exit ${rc} ================================================ FILE: Extras/Cmd4Scripts/Examples/AirPurifier.js ================================================ #!/usr/bin/env node // State.js // // Description: // This script *CAN* be called by the HomeBridge plugin Cmd4 as defined in your config.json // file. The purpose is to fake the existance of an accessory or to be modified by you // such that acessories can Get/Set characteristics as defined in the HomeKit Accessory // Specifications. // // Plugin Installation: // - npm install [-g] Cmd4 // - mkdir $HOME/.homebridge/Cmd4Scripts // - cp State.js $HOME/.homebridge/Cmd4Scripts/State.js // - chmod u+x $HOME/.homebridge/Cmd4Scripts/State.js // // Parameters are: // Get // Set // // Note 1: These paramaters match the those of the Cmd4 plugin. // Note 2: All characteristics of the HomeKit Accessory Protocol (HAP) // specifications are supported, except for camera streaming; // Though I have found some like LockManagement are not within IOS. // Side Note: The Eve app is nicer, try it. // Note 3: The Hap spec is quoted throughout, but this is not the spec so // this is not gauranteed to be correct. // Note 4: In this latest version, State.js does not care which characteristic // belongs with a specific accessssory for the purpose of creating // custom accessories. IOS would not like this, but if you use // the provided config.json file that defines the corrrect characteristics, // this will not matter. // // How it works: // A characteristic value is stored in the $HOME/.homebridge/Cmd4Scripts/Cmd4States in // the file named "Status + _ " and is returned or // written to based on the option. // // For example: node State.js Set My_Door MotionDetected 0 // will create the file .homebridge/Cmd4Scripts/Cmd4States/My_Door_MotionDetected // with the contents of "0" // Note: See how this is run from $HOME (Important!) // // Environmental Variables Used: // $HOME - to create path to .homebridge // // // Interesting fact. If you use characteristics as is, and define all possible accessories in your config.json // file, they will all appear fully functional within HomeKit. In this way you // can play with HomeKit and not have any physical devices at all. Cool Eh! // // *IMPORTANT* - I cannot audit commented constants. The lib/CMD4_DEVICE_ENUM.js is what is used // for constant translations. 'use strict'; // FileSystem requirements. var fs = require('fs'); var path = require('path'); var os = require('os'); // The files created within Cmd4StatesPath contain just a value of the accessories last state, // so they are very small in size. var Cmd4StatesPath = path.join(os.homedir(), ".homebridge/Cmd4Scripts/Cmd4States"); var length = process.argv.length; var device = ""; var io = ""; var characteristic = ""; var option = ""; if ( length == 2 ) process.exit( 0 ); if ( length <= 2 ) { process.stdout.write( `Usage: ${ process.argv[ 0 ] } ` ); process.stdout.write( ` ${ process.argv[ 0 ] } ` ); process.exit(-1); } if ( length >= 2 ) io = process.argv[ 2 ]; if ( length >= 3 ) device = process.argv[ 3 ]; if ( length >= 4 ) characteristic = process.argv[ 4 ]; if ( length >= 5 ) option = process.argv[ 5 ]; // Placing the states in a subdirectory makes things look cleaner. // Some platforms require an exception handler const mkdirSync = function( dirPath ) { try { fs.mkdirSync( dirPath ) } catch ( err ) { if ( err.code !== 'EEXIST' ) { process.stdout.write( `mkdir failed: ${ dirPath }`); throw err; } else { // directory already exists - OK } } } mkdirSync( Cmd4StatesPath ); // Such a simple way to store state information that is small and fast! // Put exception handling here too. Just in case! function writeData( a, b,c ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; try { fs.writeFileSync( fn,c ); } catch (err) { if ( err.code !== 'EEXIST' ) { process.stdout.write( `write data failed: ${ fn } data: ${ c }` ); throw err; } else { // file already exists - OK } } } // Read the state information. If there is none, just return what // was expected. // Put exception handling here too. Just in case! function readData( a, b ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; c = ""; try { c = String( fs.readFileSync( fn, 'utf8' ) ); } catch (err) { if ( err.code === 'ENOENT' ) { // This is OK. just return what was expected. return c; } else { if ( err.code !== 'EEXIST' ) { process.stdout.write( `read data failed: ${ fn }` ); throw err; } else { // file already exists - OK } } } return c; } var c = ""; switch( io ) { case "Get": { switch( characteristic ) { case "Active": // 3 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); // UUID: 000000B0-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.active // Permissions: Paired Write, Paired Read, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 1 // Step Value: 1 // Valid Values // 0 - "INACTIVE" // 1 - "ACTIVE" break; } case "CurrentAirPurifierState": // 34 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "2" ); else process.stdout.write( `"${ c }"` ); // UUID: 000000A9-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.air-purifier.state.current // Permissions: Paired Read, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 2 // Step Value: 1 // Valid Values: // 0 - "INACTIVE" // 1 - "IDLE" // 2 - "PURIFYING_AIR" break; } case "LockPhysicalControls": // 86 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); // UUID: 000000A7-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.lock-physical-controls // Permissions: Paired Write,Paired Read, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 1 // Step Value: 1 // Valid Values: // 0 - "CONTROL_LOCK_DISABLED" - Control lock disabled // 1 - "CONTROL_LOCK_ENABLED" - Control lock enabled break; } case "Name": // 97 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( `"${ device }"` ); else process.stdout.write( `"${ c }"` ); // UUID: 00000023-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.name // Permissions: Paired Read, Notify // Format: string // Maximum Length: 64 break; } case "RotationSpeed": // 139 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); // UUID: 00000029-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.rotation.speed // Permissions: Paired Read, Paired Write, Notify // Format: float // Minimum Value: 0 // Maximum Value: 100 // Step Value: 1 // Unit: percentage break; } case "SwingMode": // 180 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); // UUID: 000000B6-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.swing-mode // Permissions: Paired Read, Notify, Paired Write // Format: uint8 // Minimum Value: 0 // Maximum Value: 1 // Step Value: 1 // Valid Values: // 0 - "SWING_DISABLED" // 1 - "SWING_ENABLED" break; } case "TargetAirPurifierState": // 181 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); // UUID: 000000A8-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.air-purifier.state.target // Permissions: Paired Write,Paired Read, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 1 // Step Value: 1 // Valid Values: // 0 - "MANUAL" // 1 - "AUTO" break; } default: process.stderr.write( `Unknown Characteristic for: ${ io } Device: ${ device } Characteristic: ${ characteristic }` ); process.exit( -1 ); } break; } // End of Switch for "Get" case "Set": { switch( characteristic ) { case "Active": // 3 { writeData( device, characteristic, option ); // Off if ( option == 0 ) { // InActive - 0 writeData( device, "CurrentAirPurifierState", 0 ); // Auto Mode - 1 writeData( device, "TargetAirPurifierState", 1 ); } else // On { // Purifying Air - 2 writeData( device, "CurrentAirPurifierState", 2 ); // Auto Mode - 1 writeData( device, "TargetAirPurifierState", 1 ); } break; } case "CurrentAirPurifierState": // 34 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "LockPhysicalControls": // 86 { writeData( device, characteristic, option ); break; } case "Name": // 97 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "RotationSpeed": // 139 { writeData( device, characteristic, option ); break; } case "SwingMode": // 180 { writeData( device, characteristic, option ); break; } case "TargetAirPurifierState": // 181 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentAirPurifierState", option); break; } default: process.stderr.write( `Unknown Characteristic for: ${ io } Device:${ device } Characteristic:${ characteristic }` ); process.exit( -1 ); } break; } // End of Switch Device for "Set" default: process.stderr.write( `Unknown IO ${ io }` ); process.exit( -1 ); } //process.stdout.write( `Say What Device: ${ device } Characteristic: ${ characteristic } Option: ${ option }` ); process.exit( 0 ); ================================================ FILE: Extras/Cmd4Scripts/Examples/AnyDevice ================================================ #!/usr/bin/env node /* AnyDevice ========= Description: ============ This script is used to fake any Cmd4 devices Get/Set requests. Most characteristics just return 0. Parameters: =========== Get < Any accessory name > < Characteristic > Set < Any accessory name > < Characteristic > < value > Notes: ===== 1) These paramaters match the those of the Cmd4 plugin. How it works: ============ A characteristic value is stored in $HOME/.homebridge/Cmd4Scripts/Cmd4States in a file named "Status_ + _ " and is returned or written to based on the option. Example: ======= ./AnyDevice Set My_Door MotionDetected 0 Files Created: ============= ${ HOME }/.homebridge/Cmd4Scripts/Cmd4States/Status_My_Door with the contents of "0", the Characteristic value of the device sent. Environmental Variables Used: ============================ $HOME - Used to get the path to .homebridge */ 'use strict'; // FileSystem requirements. var fs = require( 'fs' ); var path = require( 'path' ); var os = require( 'os' ); // The files created within Cmd4StatesPath contain just a // value of the accessories last state, so they are very // small in size. var Cmd4StatesPath = path.join( os.homedir( ), ".homebridge/Cmd4Scripts/Cmd4States" ); let length = process.argv.length; let device; let io; let characteristic; let option = ""; function showHelp( ) { process.stdout.write( `Usage: AnyDevice < Get > < AccessoryName > < Characteristic > AnyDevice < Set > < AccessoryName > < Characteristic > < Value > `); } if ( length <= 4 ) { showHelp( ); process.exit( 1); } io = process.argv[ 2 ]; device = process.argv[ 3 ]; characteristic = process.argv[ 4 ]; if ( io == "Set" ) { if ( length > 5 ) { option = process.argv[ 5 ]; } else { showHelp( ); process.exit( 1 ); } } // Placing the states in a subdirectory makes things look cleaner. // Some platforms require an exception handler const mkdirSync = function ( dirPath ) { try { fs.mkdirSync( dirPath ) } catch ( err ) { if ( err.code !== 'EEXIST' ) { process.stdout.write( "mkdir failed: " + dirPath ); throw err; } else { // directory already exists - OK } } } mkdirSync( Cmd4StatesPath ); // Such a simple way to store state information that is small and fast! // Put exception handling here too. Just in case! function writeData( a, b, c ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; try { fs.writeFileSync( fn,c ); } catch ( err ) { if ( err.code !== 'EEXIST' ) { process.stdout.write( "write data failed: " + fn + " data:" + c ); throw err; } else { // file already exists - OK } } } // Read the state information. If there is none, just return what // was expected. // Put exception handling here too. Just in case! function readData( a,b ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; c = ""; try { c = String( fs.readFileSync( fn, 'utf8' ) ); } catch ( err ) { if ( err.code === 'ENOENT' ) { // This is OK. just return what was expected. return c; } else { if ( err.code !== 'EEXIST' ) { process.stdout.write( "read data failed: " + fn ); throw err; } else { // file already exists - OK } } } return c; } let c = ""; switch( io ) { case "Get": { c = readData( device, characteristic ); switch( characteristic ) { // Characteristics who should get a 1 return case "AccessoryFlags": case "AirQuality": case "DayoftheWeek": case "OpticalZoom": case "Reachable": { if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( c ); break; } case "AccessoryIdentifier": { if ( c == "" ) process.stdout.write( "TLB" ); else process.stdout.write( c ); break; } // Characteristics who should get a larger value return case "AirParticulateDensity": case "BatteryLevel": case "Brightness": case "CoolingThresholdTemperature": case "ColorTemperature": case "CurrentAmbientLightLevel": case "CurrentRelativeHumidity": case "CurrentTemperature": case "FilterLifeLevel": case "HeartBeat": case "HeatingThresholdTemperature": case "Hue": case "LockManagementAutoSecurityTimeout": case "NitrogenDioxideDensity": case "OzoneDensity": case "PM10Density": case "PM2_5Density": case "SulphurDioxideDensity": case "VOCDensity": { // Make things a little different between devices if ( c == "" ) process.stdout.write( Math.floor(Math.random() * 60) + 20 ); else process.stdout.write( c ); break; } case "CurrentTime": { if ( c == "" ) process.stdout.write( "11:15" ); else process.stdout.write( c ); break; } case "FirmwareRevision": case "Version": case "HardwareRevision": { if ( c == "" ) process.stdout.write( "100.1.1" ); else process.stdout.write( c ); break; } case "Manufacturer": case "Model": case "SerialNumber": case "Name": case "ConfiguredName": { if ( c == "" ) process.stdout.write( device ); else process.stdout.write( c ); break; } default: if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( c ); process.exit( 0 ); } break; } // End of Switch for "Get" case "Set": { switch( characteristic ) { case "Active": case "On": { writeData( device, characteristic, option ); switch( option ) { case 0: case false: case "INACTIVE": writeData( device, "RotationSpeed", 0 ); // Set to INACTIVE writeData( device, "CurrentAirPurifierState", 0 ); break; case 1: case true: case "ACTIVE": writeData( device, "RotationSpeed", 6 ); // Set to IDLE writeData( device, "CurrentAirPurifierState", 1 ); break; } break; } case "SecuritySystemTargetState": { writeData( device, characteristic, option ); // Set to done writeData( device,"SecuritySystemCurrentState", option ); break; } case "TargetAirPurifierState": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentAirPurifierState", option ); break; } case "TargetAirQuality": { writeData( device, characteristic, option ); // Set to done writeData( device, "AirQuality", option ); break; } case "TargetDoorState": { writeData( device, characteristic, option ); // Set to Done writeData( device, "CurrentDoorState", option ); break; } case "TargetFanState": { writeData( device, characteristic, option ); // Fake it Done writeData( device, "CurrentFanState", option ); break; } case "TargetHeaterCoolerState": { writeData( device, characteristic, option ); // Fake it Done writeData( device, "CurrentHeaterCoolerState", option ); break; } case "TargetHeatingCoolingState": { writeData( device, characteristic, option ); // Fake it Done writeData( device, "CurrentHeatingCoolingState", option ); break; } case "TargetHorizontalTiltAngle": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentHorizontalTiltAngle", option ); break; } case "TargetHumidifierDehumidifierState": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentHumidifierDehumidifierState", option ); break; } case "TargetMediaState": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentMediaState", option ); break; } case "TargetPosition": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentPosition", option ); break; } case "TargetRelativeHumidity": { writeData( device, characteristic, option ); // Set to done writeData( device, "CurrentRelativeHumidity", option ); break; } case "TargetSlatState": { writeData( device, characteristic, option ); // Set to done writeData( device, "CurrentSlatState", option ); break; } case "TargetTemperature": { writeData( device, characteristic, option ); // Fake it writeData( device, "CurrentTemperature", option ); break; } case "TargetTiltAngle": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentTiltAngle", option ); break; } case "TargetVerticalTiltAngle": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentVerticalTiltAngle", option ); break; } case "TargetVisibilityState": { writeData( device, characteristic, option ); // Set to done writeData( device,"CurrentVisibilityState", option ); break; } default: writeData( device, characteristic, option ); } break; } // End of Switch Device for "Set" default: console.error( "Unknown IO" + io ); process.exit( -1 ); } process.exit( 0 ); ================================================ FILE: Extras/Cmd4Scripts/Examples/DoorLock.sh ================================================ #!/bin/sh # # In this example we see a Raspberry Pi with a gpio pin that triggers # a lock mechanism. The lock is momentary. You should configure the GPIO by executing: # # echo 24 > /sys/class/gpio/export # echo "out" > /sys/class/gpio/gpio24/direction # # The corresponding config.json is # # { # "type": "LockMechanism", # "displayName": "Front Door", # "lockCurrentState": "SECURED", # "lockTargetState": "SECURED", # "name": "Front Door", # "manufacturer": "XYZ", # "model": "XYZ", # "serialNumber": "GPIO 24", # "polling": [ # { # "characteristic": "lockCurrentState", # "interval": 5, # "timeout": 4900 # }, # { # "characteristic": "lockTargetState", # "interval": 5, # "timeout": 4900 # } # ], # "stateChangeResponseTime": 0.2, # "state_cmd": "sh /homebridge/DoorLock.sh" # } #!/bin/sh STATE_FILE="/dev/shm/DoorLock.state" if [ ! -f "$STATE_FILE" ]; then echo 1 > $STATE_FILE fi STATE=$(cat $STATE_FILE) if [ "$1" = "Get" ]; then case $3 in "LockCurrentState") echo $STATE ;; "LockTargetState") echo $STATE echo 1 > $STATE_FILE ;; esac echo 0 > /sys/class/gpio/gpio24/value exit 0 fi if [ "$1" = "Set" ]; then case $3 in "LockTargetState") if [ "$4" = "UNSECURED" ]; then echo 0 > $STATE_FILE echo 1 > /sys/class/gpio/gpio24/value sleep 0.1 fi ;; esac echo 0 > /sys/class/gpio/gpio24/value exit 0 fi exit 66 ================================================ FILE: Extras/Cmd4Scripts/Examples/ExampleJavaScript_template.js ================================================ #!/usr/bin/env node // ExampleScript_template.js // // Description: // This script is a goood starting place for you to create Cmd4 Scripts // of your own // // Parameters are: // Get < Any accessory name > < Characteristic > // Set < Any accessory name > < Characteristic > < value > // // Note 1: These paramaters match the those of the Cmd4 plugin. // A full lost of supported devices and characteristics can be // found at: // https://ztalbot2000.github.io/homebridge-cmd4 // // How it works: // The Cmd4 plugin will call this script to retrieve those states // you have defined as not Cached to Get/Set your devices characteristic // states. // // For example: // node ExampleScript_template.js Set My_Door TargetDoorState 0 // or // node ExampleScript.js Get My_Door CurrentDoorState // // 'use strict'; var length = process.argv.length; var device = "My_Door"; var io = ""; var characteristic = ""; var option = ""; if ( length == 2 ) process.exit( 0 ); if ( length <= 2 ) { console.log( "Usage: " + process.argv[0] + " < Get > < AccessoryName > < Characteristic >" ); console.log( " " + process.argv[0] + " < Set > < AccessoryName > < Characteristic > < Value >" ); process.exit( -1 ); } if ( length >= 2 ) io = process.argv[2]; if ( length >= 3 ) device = process.argv[3]; if ( length >= 4 ) characteristic = process.argv[4]; if ( length >= 5 ) option = process.argv[5]; var c = ""; switch( io ) { case "Get": { switch( characteristic ) { case "CurrentDoorState": { console.log( 0 ); // See https://ztalbot2000.github.io/homebridge-cmd4 // For the possible values and characteristics // available per device. It will show somethink like: // Valid Values: // 0 - "Open. The door is fully open." // 1 - "Closed. The door is fully closed." // 2 - "Opening. The door is actively opening." // 3 - "Closing. The door is actively closing." // 4 - "Stopped. The door is not moving, and it is not fully // open nor fully closed." // 5-255 - "Reserved" break; } case "TargetDoorState": { console.log( 0 ); break; } case "ObstructionDetected": { console.log ( 0 ); break; } case "LockCurrentState": { console.log ( 0 ); break; } default: console.error( "Unhandled characteristic for:" + io + " Device:" + device + " Characteristic:" + characteristic ); process.exit( -1 ); } break; } // End of Switch for "Get" case "Set": { switch( characteristic ) { case "CurrentDoorState": { // Current Door State is not settable. The // call would be to TargetDoorState. This is here // for debugging only. break; } case "TargetDoorState": { // Do something of your own here. break; } case "ObstructionDetected": { // Obstruction Detected is not settable. It // call is a read-only characteristic. This is here // for debugging only. break; } case "LockCurrentState": { // Lock Current State is not settable. It // call is a read-only characteristic. This is here // for debugging only. break; } default: console.error( "UnHandled Characteristic for:" + io + " Device:" + device + " Characteristic:" + characteristic ); process.exit( -1 ); } break; } // End of Switch Device for "Set" default: console.error( "Unknown IO" + io ); process.exit( -1 ); } //console.log( "Say What Device:" + device + " Characteristic:" + characteristic + " Option:" + option ); // You must exit with a zero status, confirming the script rannsuccessfully. process.exit( 0 ); ================================================ FILE: Extras/Cmd4Scripts/Examples/ExampleShellScript_template.sh ================================================ #!/bin/bash # ExampleScript_template.sh # # Description: # This script is a goood starting place for you to create Cmd4 Scripts # of your own # # Parameters are: # Get < Any accessory name > < Characteristic> # Set < Any accessory name > < Characteristic> < value > # # Note 1: These paramaters match the those of the Cmd4 plugin. # A full lost of supported devices and characteristics can be # found at: # https://ztalbot2000.github.io/homebridge-cmd4 # # How it works: # # The Cmd4 plugin will call this script to retrieve those states # you have defined as not Cached to Get/Set your devices characteristic # states. # # For example: # bash ExampleScript_template.sh Set My_Door TargetDoorState 0 # or # bash ExampleScript.sh Get My_Door CurrentDoorState # set -e # Exit immediately for unbound variables. set -u length=$# device="" io="" characteristic="" option="" if [ $length -le 1 ]; then printf "Usage: $0 Get < AccessoryName > < Characteristic >\n" printf "Usage: $0 Set < AccessoryName > < Characteristic > < Value >\n" exit -1 fi # printf "args =$#\n" # debug # printf "arg1 =$1\n" # debug if [ $length -ge 1 ]; then io=$1 # printf "io=$io\n" # debug fi if [ $length -ge 2 ]; then device=$2 # printf "device = ${device}\n" # debug fi if [ $length -ge 3 ]; then characteristic=$3 # printf "Characteristic = ${characteristic}\n" # debug fi if [ $length -ge 4 ]; then option=$4 # printf "option = ${option}\n" # debug fi if [ "${io}" == "Get" ]; then case $characteristic in 'CurrentDoorState') printf "0\n" # Door is open # See https://ztalbot2000.github.io/homebridge-cmd4 # For the possible values and characteristics # available per device. It will show somethink like: # Valid Values: # 0 - "Open. The door is fully open." # 1 - "Closed. The door is fully closed." # 2 - "Opening. The door is actively opening." # 3 - "Closing. The door is actively closing." # 4 - "Stopped. The door is not moving, and it is not fully # open nor fully closed." # 5-255 - "Reserved" exit 0 ;; 'TargetDoorState') printf "0\n" exit 0 ;; 'ObstructionDetected') printf "0\n" exit 0 ;; 'LockCurrentState') printf "0\n" exit 0 ;; *) printf "UnHandled Get ${device} Characteristic ${characteristic}\n" exit -1 ;; esac fi if [ "${io}" == 'Set' ]; then case $characteristic in 'CurrentDoorState') # Current Door State is not settable. The # call would be to TargetDoorState. This is here # for debugging only. exit 0 ;; 'TargetDoorState') # Do something of your own here. exit 0 ;; 'ObstructionDetected') # Obstruction Detected is not settable. It # call is a read-only characteristic. This is here # for debugging only. exit 0 ;; 'LockCurrentState') # Lock Current State is not settable. It # call is a read-only characteristic. This is here # for debugging only. exit 0 ;; *) printf "UnHandled Set GarageDoorOpenner Characteristic ${characteristic}" exit -1 ;; esac fi printf "Unknown io command ${io}\n" exit -1 ================================================ FILE: Extras/Cmd4Scripts/Examples/PS4.sh ================================================ #!/bin/bash # # This Cmd4 example demonstrates a script that is compatible with # cmdSwitch2's example of controlling a PS4. # # Your Cmd4 .homebridge/.config.json file would have a state_cmd like: # state_cmd: ".homebridge/Cmd4Scripts/Examples/PS4.sh" # # Testing from the shell prompt: # ./PS4.sh Get PS4 On # or # ./PS4.sh Set PS4 On 1 # or # ./PS4.sh Set PS4 On 0 # Exit immediately if a command exits with a non-zero status set -e # Check if the first parameter to this script was "Get" for getting an accessory's # specific attribute. if [ "$1" = "Get" ]; then # Normally we would exit immediately if a command fails with a non-zero status. # In this case ps4-waker can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e ps4-waker search | grep -i '200 Ok' >> /dev/null 2>&1 rc=$? # Exit immediately if a command exits with a non-zero status set -e # Check if we got the message '200 OK' meaning the accessory is # on the network by seeing if the return code of the above command passed or # failed. if [ "$rc" = "0" ]; then # The message was recieved so the target is up, sending a '1' (true), like # a binary number is, back to Cmd4. echo "1" # Exit this script positivitely. exit 0 else # The message was not recieved so the target must be down, sending a '0' (false), like # a binary number is, back to Cmd4. echo "0" # Exit this script positivitely, even though ps4-waker failed. exit 0 fi fi # Check if the first parameter to this script was "Set" for setting an accessory's # specific attribute. if [ "$1" = "Set" ]; then # $2 would be the name of the accessory. # $3 would be the accessory's charactersistic 'On'. # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is. # $4 would be 'true' for 'On' and 'false' for 'Off' with # outputConstants=true in your .homebridge/.config.json file. # Handle the Set 'On' attribute of the accessory if [ "$3" = "On" ]; then # If the accessory is to be set on if [ "$4" = "1" ]; then # Normally we would exit immediately if a command fails with a non-zero status. # In this case ps4-waker can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e # Execute the on command ps4-waker >> /dev/null 2>&1 # keep the result of the on/off command rc=$? # Exit immediately if a command exits with a non-zero status set -e else # Normally we would exit immediately if a command fails with a non-zero status. # In this case ps4-waker can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e # Execute the off command ps4-waker standby >> /dev/null 2>&1 # keep the result of the on/off command rc=$? # Exit immediately if a command exits with a non-zero status set -e fi # Check if the on/off command had a positive return status. if [ "$rc" = "0" ]; then # The on/off command was successful, so exit successfully. exit 0 else # The on/off comand had a failure result. Exit with that result. # Exit this script positivitely, even though ping failed. exit $rc fi fi fi # The proper arguments to this script were not passed to it so end with a failure exit status. exit 666 ================================================ FILE: Extras/Cmd4Scripts/Examples/PS5.sh ================================================ #!/bin/bash # # This Cmd4 script uses playactor to turn on/off a PS5. # # You will need to install playactor and have the command in a global PATH # or modify this script with its actual PATH. # # Your Cmd4 .homebridge/.config.json file would have a state_cmd like: # state_cmd: ".homebridge/Cmd4Scripts/Examples/PS5.sh" # # Testing from the shell prompt: # ./PS5.sh Get PS5 On # or # ./PS5.sh Set PS5 On 1 # or # ./PS5.sh Set PS5 On 0 set -e # Exit immediately for unbound variables. set -u # Passed in Args length=$# device="" io="" characteristic="" if [ $length -le 2 ]; then echo "Usage: $0 Get < AccessoryName > < characteristic >" echo "Usage: $0 Set < AccessoryName > < characteristic > < Value >" exit 199 fi if [ $length -ge 1 ]; then io=$1 fi if [ $length -ge 2 ]; then device=$2 fi if [ $length -ge 3 ]; then characteristic=$3 fi # For "Get" Directives if [ "$io" = "Get" ]; then case "$characteristic" in On ) # Normally we would exit immediately if a command fails with a non-zero status. # In this case playactor can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e # Check if we got the message '200 OK' meaning the accessory is # on the network by seeing if the return code of the above command passed or # failed. playactor check | grep -i '200 Ok'>> /dev/null 2>&1 rc=$? set -e if [ "$rc" = "0" ]; then # The message was recieved so the target is up, sending a '1' (true), like # a binary number is, back to Cmd4. stdbuf -o0 -e0 echo 1 exit 0 else # The message was not recieved so the target must be down, sending a '0' (false), like # a binary number is, back to Cmd4. stdbuf -o0 -e0 echo 0 exit 0 fi ;; *) echo "Unhandled Get characteristic $characteristic" >&2 exit 109 ;; esac fi # For "Set" Directives if [ "$io" = "Set" ]; then value="1" if [ $length -ge 4 ]; then value=$4 else echo "No value specified for set" >&2 exit 199 fi case "$characteristic" in On ) # Normally we would exit immediately if a command fails with a non-zero status. # In this case playactor can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e if [ "$value" = "1" ]; then # Execute the on command sudo playactor wake exit 0 else # Execute the off command sudo playactor standby exit 0 fi ;; *) echo "Unhandled Set characteristic $characteristic" >&2 exit 109 ;; esac fi echo "Unhandled $io $device $characteristic" >&2 exit 150 ================================================ FILE: Extras/Cmd4Scripts/Examples/SecuritySystem.js ================================================ #!/usr/bin/env node // SecuritySystem.js // // Description: // This script *CAN* be called by the HomeBridge plugin Cmd4 as defined in your config.json // file. The purpose is to fake the existance of an Security System as best it can. // // Note: Security Systems are tricky to fake since the securitySysytemCurrentState // Does not align with the SecuritySystemTargetState. The only way to trigger // the alarm would be: // node ./SecuritySystem.js Set My_System SecuritySystemCurrentState 4 // // Likewise to disarm // node ./SecuritySystem.js Set My_System SecuritySystemCurrentState 3 // // Note 1: The Hap spec is quoted throughout, but this is not the spec so // this is not gauranteed to be correct. // // How it works: // A characteristic value is stored in the $HOME/.homebridge/Cmd4Scripts/Cmd4States in // the file named "Status + _ " and is returned or // written to based on the option. // // For example: node SecuritySystem.js Set My_System SecuritySystemCurrentState 0 // will create the file: // .homebridge/Cmd4Scripts/Cmd4States/Status_My_System_SecuritySystemCurrentState // with the contents of "0" // // Environmental Variables Used: // $HOME - to create path to .homebridge // // The following is an example config.json entry for this script. /* { "platform": "Cmd4", "name": "Cmd4", "debug": false, "statusMsg": true, "accessories": [ { "type": "SecuritySystem", "displayName": "My_SecuritySystem", "securitySystemCurrentState": "AWAY_ARM", "securitySystemTargetState": "AWAY_ARM", "interval": 30, "timeout": 4900, "polling": [ { "characteristic": "securitySystemCurrentState" }, { "characteristic": "securitySystemTargetState" } ], "stateChangeResponseTime": 1, "state_cmd": "node ./homebridge/Cmd4Scripts/SecuritySystem.js" } } */ 'use strict'; // FileSystem requirements. var fs = require('fs'); var path = require('path'); var os = require('os'); // The files created within Cmd4StatesPath contain just a value of the accessories last state, // so they are very small in size. var Cmd4StatesPath = path.join(os.homedir(), ".homebridge/Cmd4Scripts/Cmd4States"); var length = process.argv.length; var device = ""; var io = ""; var characteristic = ""; var option = ""; if ( length == 2 ) process.exit( 0 ); if ( length <= 2 ) { process.stdout.write( `Usage: ${ process.argv[ 0 ] } ` ); process.stdout.write( ` ${ process.argv[ 0 ] } ` ); process.exit(-1); } if ( length >= 2 ) io = process.argv[ 2 ]; if ( length >= 3 ) device = process.argv[ 3 ]; if ( length >= 4 ) characteristic = process.argv[ 4 ]; if ( length >= 5 ) option = process.argv[ 5 ]; // Placing the states in a subdirectory makes things look cleaner. // Some platforms require an exception handler const mkdirSync = function( dirPath ) { try { fs.mkdirSync( dirPath ) } catch ( err ) { if ( err.code !== 'EEXIST' ) { process.stdout.write( `mkdir failed: ${ dirPath }`); throw err; } else { // directory already exists - OK } } } mkdirSync( Cmd4StatesPath ); // Such a simple way to store state information that is small and fast! // Put exception handling here too. Just in case! function writeData( a, b,c ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; try { fs.writeFileSync( fn,c ); } catch (err) { if ( err.code !== 'EEXIST' ) { process.stdout.write( `write data failed: ${ fn } data: ${ c }` ); throw err; } else { // file already exists - OK } } } // Read the state information. If there is none, just return what // was expected. // Put exception handling here too. Just in case! function readData( a, b ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; c = ""; try { c = String( fs.readFileSync( fn, 'utf8' ) ); } catch (err) { if ( err.code === 'ENOENT' ) { // This is OK. just return what was expected. return c; } else { if ( err.code !== 'EEXIST' ) { process.stdout.write( `read data failed: ${ fn }` ); throw err; } else { // file already exists - OK } } } return c; } var c = ""; switch( io ) { case "Get": { switch( characteristic ) { case "Name": // 97 Optional Characteristic { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( `"${ device }"` ); else process.stdout.write( `"${ c }"` ); // UUID: 00000023-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.name // Permissions: Paired Read, Notify // Format: string // Maximum Length: 64 break; } case "SecuritySystemCurrentState": // 143 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); // UUID: 00000066-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.security-system-state.current // Permissions: Paired Read, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 4 // Step Value: 1 // Valid Values: // 0 - "STAY_ARM" - The home is occupied and the residents are // active. e.g. morning or evenings" // 1 - "AWAY_ARM" - The home is unoccupied" // 2 - "NIGHT ARM" - The home is occupied and the residents are sleeping" // 3 - "DISARMED" // 4 - "ALARM_TRIGGERED" // 5 - 255 - Reserved break; } case "SecuritySystemTargetState": // 144 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); // UUID: 00000067-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.security-system-state.target // Permissions: Paired Read, Paired Write, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 3 // Step Value: 1 // Valid Values: // 0 - "STAY_ARM" - The home is occupied and the residents // are active. e.g. morning or evenings // 1 - "AWAY_ARM" - The home is unoccupied // 2 - "NIGHT_ARM" - The home is occupied and the residents are sleeping // // 3 - 255 - Reserved break; } case "SecuritySystemAlarmType": // 142 Optional Characteristic { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); // UUID: 0000008E-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.security-system.alarm-type // Permissions: Paired Read, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 1 // Step Value: 1 // Valid Values: // 0 - When alert cleared // 1 - Unknown Cause break; } case "StatusFault": // 163 Optional Characteristic { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); // UUID: 00000077-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.status-fault // Permissions: Paired Read, Notify // Format: uint8 // Minimum Value: 0 // Maximum Value: 1 // Step Value: 1 // Valid Values: // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" break; } case "StatusTampered": // 166 Optional Characteristic { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); // UUID: 0000007A-0000-1000-8000-0026BB765291 // Type: public.hap.characteristic.status-tampered // Permissions: Paired Read, Notify // Format: uint8 // Valid Values: // 0 - "NOT_TAMPERED" - Accessory is not tampered" // 1 - "TAMPERED" - Accessory is tampered with break; } default: process.stderr.write( `Unknown Characteristic for: ${ io } Device: ${ device } Characteristic: ${ characteristic }` ); process.exit( -1 ); } break; } // End of Switch for "Get" case "Set": { switch( characteristic ) { case "Name": // 97 Optional Characteristic { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SecuritySystemAlarmType": // 142 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SecuritySystemCurrentState": // 143 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SecuritySystemTargetState": // 144 { writeData( device, characteristic, option ); // Set to done writeData(device,"SecuritySystemCurrentState", option); break; } case "StatusFault": // 163 Optional Characteristic { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "StatusTampered": // 166 Optional Characteristic { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } default: process.stderr.write( `Unknown Characteristic for: ${ io } Device:${ device } Characteristic:${ characteristic }` ); process.exit( -1 ); } break; } // End of Switch Device for "Set" default: process.stderr.write( `Unknown IO ${ io }` ); process.exit( -1 ); } //process.stdout.write( `Say What Device: ${ device } Characteristic: ${ characteristic } Option: ${ option }` ); process.exit( 0 ); ================================================ FILE: Extras/Cmd4Scripts/Examples/advanced_ping.sh ================================================ #!/bin/sh # # This Cmd4 example demonstrates a little more advanced way of using ping to test if an # accessory is on the network by passing in the IP address to be used with the Cmd4 option # of state_cmd_suffix. # # Your Cmd4 .homebridge/.config.json file would have a state_cmd like: # state_cmd: ".homebridge/Cmd4Scripts/Examples/ping.sh" # state_cmd_suffix: "192.168.2.1" # # Testing from the shell prompt: # ./advanced_ping.sh Get My_TV On 192.168.2.1 # or # ./advanced_ping.sh Set My_TV On 1 192.168.2.1 # Exit immediately if a command exits with a non-zero status set -e # Check if the first parameter to this script was "Get" for getting an accessory's # specific attribute. if [ "$1" = "Get" ]; then # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fourth # parameter to a Get command. ip="${4}" # Normally we would exit immediately if a command fails with a non-zero status. # In this case ping can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e # $2 would be the name of the accessory # $3 would be the accessory's charactersistic 'On' # On OSX the string is returned differently than on linux. ping -c 2 -W 1 "${ip}" | sed -E 's/2 packets received/2 received/g' | grep -i '2 received' >> /dev/null rc=$? # Exit immediately if a command exits with a non-zero status set -e # Check if we got the message '2 packets recieved' meaning the accessory is # on the network by seeing if the return code of the above command passed or # failed. if [ "$rc" = "0" ]; then # The message was recieved so the target is up, sending a '1' (true), like # a binary number is, back to Cmd4. echo "1" # Exit this script positivitely. exit 0 else # The message was not recieved so the target must be down, sending a '0' (false), like # a binary number is, back to Cmd4. echo "0" # Exit this script positivitely, even though ping failed. exit 0 fi fi # Check if the first parameter to this script was "Set" for setting an accessory's # specific attribute. if [ "$1" = "Set" ]; then # $2 would be the name of the accessory. # $3 would be the accessory's charactersistic 'On'. # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is. # $4 would be 'true' for 'On' and 'false' for 'Off' with # outputConstants=true in your .homebridge/.config.json file. # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fifth # parameter to a Set command. ip="${5}" # This ping script does not do anything for set so just exit successfully. exit 0 fi # The proper arguments to this script were not passed to it so end with a failure exit status. exit 66 ================================================ FILE: Extras/Cmd4Scripts/Examples/basic_ping.sh ================================================ #!/bin/sh # # This Cmd4 example demonstrates how you can test if an accessory is on the network using ping. # # Your Cmd4 .homebridge/.config.json file would have a state_cmd like: # state_cmd: ".homebridge/Cmd4Scripts/Examples/ping.sh" # # # Testing from the shell prompt: # ./basic_ping.sh Get My_TV On # or # ./basic_ping.sh Set My_TV On 1 # Exit immediately if a command exits with a non-zero status set -e # Define the accessories IP you wish to test ip="192.168.2.1" # Check if the first parameter to this script was "Get" for getting an accessory's # specific attribute. if [ "$1" = "Get" ]; then # Normally we would exit immediately if a command fails with a non-zero status. # In this case ping can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e # $2 would be the name of the accessory # $3 would be the accessory's charactersistic 'On' # On OSX the string is returned differently than on linux. ping -c 2 -W 1 "${ip}" | sed -E 's/2 packets received/2 received/g' | grep -i '2 received' >> /dev/null rc=$? # Exit immediately if a command exits with a non-zero status set -e # Check if we got the message '2 packets recieved' meaning the accessory is # on the network by seeing if the return code of the above command passed or # failed. if [ "$rc" = "0" ]; then # The message was recieved so the target is up, sending a '1' (true), like # a binary number is, back to Cmd4. echo "1" # Exit this script positivitely. exit 0 else # The message was not recieved so the target must be down, sending a '0' (false), like # a binary number is, back to Cmd4. echo "0" # Exit this script positivitely, even though ping failed. exit 0 fi fi # Check if the first parameter to this script was "Set" for setting an accessory's # specific attribute. if [ "$1" = "Set" ]; then # $2 would be the name of the accessory. # $3 would be the accessory's charactersistic 'On'. # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is. # $4 would be 'true' for 'On' and 'false' for 'Off' with # outputConstants=true in your .homebridge/.config.json file. # This ping script does not do anything for set so just exit successfully. exit 0 fi # The proper arguments to this script were not passed to it so end with a failure exit status. exit 66 ================================================ FILE: Extras/Cmd4Scripts/Examples/middleWare.sh ================================================ #!/bin/bash result=$(node .homebridge/Cmd4Scripts/State.js $* 2>&1) echo $( date ) >> /tmp/Cmd4.log echo $* >> /tmp/Cmd4.log echo $result | tee -a /tmp/Cmd4.log ================================================ FILE: Extras/Cmd4Scripts/Examples/wakeonlan.sh ================================================ #!/bin/bash # # This Cmd4 example demonstrates a script that can be used for a wakeonlan # scenario. It is a port of the cmdSwitch2 example and is more for a Windows # PC. # # Your Cmd4 .homebridge/.config.json file would have a state_cmd like: # state_cmd: ".homebridge/Cmd4Scripts/Examples/wakeonlan.sh" # state_cmd_suffix: "192.168.2.66 dc:a6:32:40:de:7c" # # # Testing from the shell prompt: # ./wakeonlan.sh Get HTPC On 192.168.2.66 dc:a6:32:40:de:7c # or # ./wakeonlan.sh Set HTPC On 1 192.168.2.66 dc:a6:32:40:de:7c # or # ./wakeonlan.sh Set HTPC On 0 192.168.2.66 dc:a6:32:40:de:7c # Exit immediately if a command exits with a non-zero status set -e # Check if the first parameter to this script was "Get" for getting an accessory's # specific attribute. if [ "$1" = "Get" ]; then # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fourth # parameter to a Get command. ip="${4}" # Cmd4 will pass the MAC Address in the config.json defined by state_cmd_suffix as the fifth # parameter to a Get command. macAddress="${5}" # Normally we would exit immediately if a command fails with a non-zero status. # In this case ping can fail and we would rely on the failing exit status to # tell Cmd4 that the accessory is not on the network. That would be the prefered # thing to do. However for this example we are going to output '0' (false) so # that you can see the '0' on the console telling us that the accessory is not # on the network. set +e # On OSX the string is returned differently than on linux. ping -c 2 -W 1 "${ip}" | sed -E 's/2 packets received/2 received/g' | grep -i '2 received' >> /dev/null rc=$? # Exit immediately if a command exits with a non-zero status set -e # Check if we got the message '2 packets recieved' meaning the accessory is # on the network by seeing if the return code of the above command passed or # failed. if [ "$rc" = "0" ]; then # The message was recieved so the target is up, sending a '1' (true), like # a binary number is, back to Cmd4. echo "1" # Exit this script positivitely. exit 0 else # The message was not recieved so the target must be down, sending a '0' (false), like # a binary number is, back to Cmd4. echo "0" # Exit this script positivitely, even though ping failed. exit 0 fi fi # Check if the first parameter to this script was "Set" for setting an accessory's # specific attribute. if [ "$1" = "Set" ]; then # $2 would be the name of the accessory. # $3 would be the accessory's charactersistic 'On'. # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is. # $4 would be 'true' for 'On' and 'false' for 'Off' with # outputConstants=true in your .homebridge/.config.json file. # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fifth # parameter to a Set command. ip="${5}" # Cmd4 will pass the MAC Address in the config.json defined by state_cmd_suffix as the sixth # parameter to a Set command. macAddress="${6}" # Handle the Set 'On' attribute of the accessory if [ "$3" = "On" ]; then # If the accessory is to be set on if [ "$4" = "1" ]; then # Execute the on command wakeonlan -i ${ip} ${macAddress} >> /dev/null 2>&1 # keep the result of the on/off command rc=$? else # Execute the off command # The password is harad coded here. We use the default # Raspberry Pi password asli an example. How you handle # this unencrypted password is up to you. Note that # this command only works to a Windows box, but is the # exmple given in cmdSwitch2. # net rpc shutdown -I "${ip}" -U user%password, net rpc shutdown -I "${ip}" -U pi%raspberry >> /dev/null 2>&1 # keep the result of the on/off command rc=$? fi # Check if the on/off command had a positive return status. if [ "$rc" = "0" ]; then # The on/off command was successful, so exit successfully. exit 0 else # The on/off comand had a failure result. Exit with that result. # Exit this script positivitely, even though ping failed. exit $rc fi fi fi # The proper arguments to this script were not passed to it so end with a failure exit status. exit 66 ================================================ FILE: Extras/Cmd4Scripts/State.js ================================================ #!/usr/bin/env node // State.js // // Description: // This script *CAN* be called by the HomeBridge plugin Cmd4 as defined in your config.json // file. The purpose is to fake the existance of an accessory or to be modified by you // such that acessories can Get/Set characteristics as defined in the HomeKit Accessory // Specifications. // // Plugin Installation: // - npm install [-g] Cmd4 // - mkdir $HOME/.homebridge/Cmd4Scripts // - cp State.js $HOME/.homebridge/Cmd4Scripts/State.js // - chmod u+x $HOME/.homebridge/Cmd4Scripts/State.js // // Parameters are: // Get // Set // // Note 1: These paramaters match the those of the Cmd4 plugin. // Note 2: All characteristics of the HomeKit Accessory Protocol (HAP) // specifications are supported, except for camera streaming; // Though I have found some like LockManagement are not within IOS. // Side Note: The Eve app is nicer, try it. // Note 3: The Hap spec is quoted throughout, but this is not the spec so // this is not gauranteed to be correct. // Note 4: In this latest version, State.js does not care which characteristic // belongs with a specific accessssory for the purpose of creating // custom accessories. IOS would not like this, but if you use // the provided config.json file that defines the corrrect characteristics, // this will not matter. // // How it works: // A characteristic value is stored in the $HOME/.homebridge/Cmd4Scripts/Cmd4States in // the file named "Status + _ " and is returned or // written to based on the option. // // For example: node State.js Set My_Door MotionDetected 0 // will create the file .homebridge/Cmd4Scripts/Cmd4States/My_Door_MotionDetected // with the contents of "0" // Note: See how this is run from $HOME (Important!) // // Environmental Variables Used: // $HOME - to create path to .homebridge // // // Interesting fact. If you use characteristics as is, and define all possible accessories in your config.json // file, they will all appear fully functional within HomeKit. In this way you // can play with HomeKit and not have any physical devices at all. Cool Eh! // // *IMPORTANT* - I cannot audit commented constants. The lib/CMD4_DEVICE_ENUM.js is what is used // for constant translations. 'use strict'; // FileSystem requirements. var fs = require('fs'); var path = require('path'); var os = require('os'); // The files created within Cmd4StatesPath contain just a value of the accessories last state, // so they are very small in size. var Cmd4StatesPath = path.join(os.homedir(), ".homebridge/Cmd4Scripts/Cmd4States"); var length = process.argv.length; var device = ""; var io = ""; var characteristic = ""; var option = ""; if ( length == 2 ) process.exit( 0 ); if ( length <= 2 ) { process.stdout.write( `Usage: ${ process.argv[ 0 ] } ` ); process.stdout.write( ` ${ process.argv[ 0 ] } ` ); process.exit(-1); } if ( length >= 2 ) io = process.argv[ 2 ]; if ( length >= 3 ) device = process.argv[ 3 ]; if ( length >= 4 ) characteristic = process.argv[ 4 ]; if ( length >= 5 ) option = process.argv[ 5 ]; // Placing the states in a subdirectory makes things look cleaner. // Some platforms require an exception handler const mkdirSync = function( dirPath ) { try { fs.mkdirSync( dirPath ) } catch ( err ) { if ( err.code !== 'EEXIST' ) { process.stdout.write( `mkdir failed: ${ dirPath }`); throw err; } else { // directory already exists - OK } } } mkdirSync( Cmd4StatesPath ); // Such a simple way to store state information that is small and fast! // Put exception handling here too. Just in case! function writeData( a, b,c ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; try { fs.writeFileSync( fn,c ); } catch (err) { if ( err.code !== 'EEXIST' ) { process.stdout.write( `write data failed: ${ fn } data: ${ c }` ); throw err; } else { // file already exists - OK } } } // Read the state information. If there is none, just return what // was expected. // Put exception handling here too. Just in case! function readData( a, b ) { var fn = Cmd4StatesPath + "/Status_" + a + "_" + b; c = ""; try { c = String( fs.readFileSync( fn, 'utf8' ) ); } catch (err) { if ( err.code === 'ENOENT' ) { // This is OK. just return what was expected. return c; } else { if ( err.code !== 'EEXIST' ) { process.stdout.write( `read data failed: ${ fn }` ); throw err; } else { // file already exists - OK } } } return c; } var c = ""; switch( io ) { case "Get": { switch( characteristic ) { case "AccessControlLevel": // 0 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( `1` ); else process.stdout.write( `"${ c }"` ); // To avoid hard coding values, characteristic definitions can be found in: // Cmd4 Github pages -> http://ztalbot2000.github.io/homebridge-cmd4 // Homebridge developer documentation -> https://developers.homebridge.io/#/ break; } case "AccessoryFlags": // 1 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "AccessoryIdentifier": // 2 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "TLB" ); else process.stdout.write( `"${ c }"` ); break; } case "Active": // 3 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "ActiveIdentifier": // 4 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "ActivityInterval": // 5 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "AdministratorOnlyAccess": // 6 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "AirParticulateDensity": // 7 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "30" ); else process.stdout.write( `"${ c }"` ); break; } case "AirParticulateSize": // 8 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "AirQuality": // 9 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "AppMatchingIdentifier": // 10 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "AudioFeedback": // 11 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "BatteryLevel": // 12 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "Brightness": // 13 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); break; } case "ButtonEvent": // 14 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CCAEnergyDetectThreshold": // 15 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "CCASignalDetectThreshold": // 16 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "CameraOperatingModeIndicator": // 17 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); break; } case "CarbonDioxideDetected": // 18 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CarbonDioxideLevel": // 19 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CarbonDioxidePeakLevel": // 20 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CarbonMonoxideDetected": // 21 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CarbonMonoxideLevel": // 22 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CarbonMonoxidePeakLevel": // 23 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Category": // 24 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "CharacteristicValueTransitionControl": // 25 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ChargingState": // 26 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ClosedCaptions": // 27 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ColorTemperature": // 28 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "ConfigureBridgedAccessory": // 29 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ConfigureBridgedAccessoryStatus": // 30 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ConfiguredName": // 31 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( `"${ device }"` ); else process.stdout.write(`"${ c }"` ); break; } case "ContactSensorState": // 32 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CoolingThresholdTemperature": // 33 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "32.4" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentAirPurifierState": // 34 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "2" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentAmbientLightLevel": // 35 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentDoorState": // 36 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentFanState": // 37 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentHeaterCoolerState": // 38 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); process.exit(0); break; } case "CurrentHeatingCoolingState": // 39 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); process.exit(0); break; } case "CurrentHorizontalTiltAngle": // 40 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentHumidifierDehumidifierState": // 41 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentMediaState": // 42 { c = readData( device, characteristic ); //validValues: // "PLAY" // "PAUSE" // "STOP" // "LOADING" // "INTERRUPTED" if ( c == "" ) process.stdout.write( "STOP" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentPosition": // 43 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentRelativeHumidity": // 44 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "60.2" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentSlatState": // 45 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentTemperature": // 46 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "22.2" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentTiltAngle": // 47 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentTime": // 48 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "11.5" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentTransport": // 49 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentVerticalTiltAngle": // 50 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CurrentVisibilityState": // 51 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "DataStreamHAPTransport": // 52 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "DataStreamHAPTransportInterrupt": // 53 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "DayoftheWeek": // 54 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "DiagonalFieldOfView": // 55 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "DigitalZoom": // 56 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "DiscoverBridgedAccessories": // 57 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "DiscoveredBridgedAccessories": // 58 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "DisplayOrder": // 59 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "EventRetransmissionMaximum": // 60 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "EventSnapshotsActive": // 61 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "EventTransmissionCounters": // 62 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "FilterChangeIndication": // 63 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "FilterLifeLevel": // 64 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "FirmwareRevision": // 65 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100.1.1" ); else process.stdout.write( `"${ c }"` ); break; } case "HardwareRevision": // 66 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100.1.1" ); else process.stdout.write( `"${ c }"` ); break; } case "HeartBeat": // 67 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "64" ); else process.stdout.write( `"${ c }"` ); break; } case "HeatingThresholdTemperature": // 68 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "25.2" ); else process.stdout.write( `"${ c }"` ); break; } case "HoldPosition": // 69 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "HomeKitCameraActive": // 70 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Hue": // 71 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "Identifier": // 77 { c = readData( device, characteristic ); if ( c == "" ){ // Each identifier needs a unique number // Match this with what is in the config.json switch( device ) { case "HDMI1": process.stdout.write( "0" ); break; case "HDMI2": process.stdout.write( "1" ); break; case "HDMI3": process.stdout.write( "2" ); break; case "HDMI4": process.stdout.write( "3" ); break; case "Netflix": process.stdout.write( "4" ); break; default: process.stdout.write( "0" ); } } else { process.stdout.write( `"${ c }"` ); } break; } case "Identify": // 73 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ImageMirroring": // 74 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ImageRotation": // 75 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "InUse": // 76 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "InputDeviceType": // 77 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "InputSourceType": // 78 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "IsConfigured": // 79 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "LeakDetected": // 80 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "LinkQuality": // 81 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ListPairings": // 82 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "LockControlPoint": // 83 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "LockCurrentState": // 84 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "LockLastKnownAction": // 85 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "LockManagementAutoSecurityTimeout": // 86 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "26" ); else process.stdout.write( `"${ c }"` ); break; } case "LockPhysicalControls": // 87 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "LockTargetState": // 88 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Logs": // 89 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "MACRetransmissionMaximum": // 90 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); break; } case "MACTransmissionCounters": // 91 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); break; } case "ManagedNetworkEnable": // 92 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ManuallyDisabled": // 93 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Manufacturer": // 94 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "Homebridge" ); else process.stdout.write( `"${ c }"` ); break; } case "Model": // 95 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "MotionDetected": // 96 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Mute": // 97 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Name": // 98 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( `"${ device }"` ); else process.stdout.write( `"${ c }"` ); break; } case "NetworkAccessViolationControl": // 99 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "NetworkClientProfileControl": // 100 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "NetworkClientStatusControl": // 101 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "NightVision": // 102 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "NitrogenDioxideDensity": // 103 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50.0" ); else process.stdout.write( `"${ c }"` ); break; } case "ObstructionDetected": // 104 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "OccupancyDetected": // 105 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "On": // 106 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "OperatingStateResponse": // 107 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "OpticalZoom": // 108 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1.0" ); else process.stdout.write( `"${ c }"` ); break; } case "OutletInUse": // 109 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "OzoneDensity": // 110 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50.0" ); else process.stdout.write( `"${ c }"` ); break; } case "PM10Density": // 111 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50.0" ); else process.stdout.write( `"${ c }"` ); break; } case "PM2_5Density": // 112 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50.0" ); else process.stdout.write( `"${ c }"` ); break; } case "PairSetup": // 113 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "PairVerify": // 114 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "PairingFeatures": // 115 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "PairingPairings": // 116 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "PasswordSetting": // 117 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "PeriodicSnapshotsActive": // 118 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "PictureMode": // 119 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "Ping": // 120 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "PositionState": // 121 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "2" ); else process.stdout.write( `"${ c }"` ); break; } case "PowerModeSelection": // 122 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "ProductData": // 123 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "ProgramMode": // 124 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ProgrammableSwitchEvent": // 125 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ProgrammableSwitchOutputState": // 126 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Reachable": // 127 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "ReceivedSignalStrengthIndication": // 128 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "ReceiverSensitivity": // 129 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "RecordingAudioActive": // 130 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "RelativeHumidityDehumidifierThreshold": // 131 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "RelativeHumidityHumidifierThreshold": // 132 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "RelayControlPoint": // 133 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "RelayEnabled": // 134 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "RelayState": // 135 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "RemainingDuration": // 136 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "RemoteKey": // 137 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "ResetFilterIndication": // 138 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "RotationDirection": // 139 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "RotationSpeed": // 140 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); break; } case "RouterStatus": // 141 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); break; } case "Saturation": // 142 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "SecuritySystemAlarmType": // 143 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SecuritySystemCurrentState": // 144 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "3" ); else process.stdout.write( `"${ c }"` ); break; } case "SecuritySystemTargetState": // 145 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SelectedAudioStreamConfiguration": // 146 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SelectedCameraRecordingConfiguration": // 147 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SelectedRTPStreamConfiguration": // 148 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SerialNumber": // 149 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "ABC001" ); else process.stdout.write( `"${ c }"` ); break; } case "ServiceLabelIndex": // 150 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ServiceLabelNamespace": // 151 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SetDuration": // 152 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "SetupDataStreamTransport": // 153 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SetupEndpoints": // 154 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SetupTransferTransport": // 155 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SignalToNoiseRatio": // 156 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SiriInputType": // 157 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SlatType": // 158 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SleepDiscoveryMode": // 159 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "SleepInterval": // 160 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "SmokeDetected": // 161 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SoftwareRevision": // 162 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "StatusActive": // 163 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "StatusFault": // 164 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "StatusJammed": // 165 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "StatusLowBattery": // 166 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "StatusTampered": // 167 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "StreamingStatus": // 168 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SulphurDioxideDensity": // 169 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50.0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedAudioRecordingConfiguration": // 170 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedAudioStreamConfiguration": // 171 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedCameraRecordingConfiguration": // 172 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedDataStreamTransportConfiguration": // 173 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedCharacteristicValueTransitionConfiguration": // 174 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedDiagnosticsSnapshot": // 175 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedRTPConfiguration": // 176 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedRouterConfiguration": // 177 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedTransferTransportConfiguration": // 178 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedVideoRecordingConfiguration": // 179 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedVideoStreamConfiguration": // 180 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SwingMode": // 181 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "100" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetAirPurifierState": // 182 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetAirQuality": // 183 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetControlList": // 184 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetControlSupportedConfiguration": // 185 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetDoorState": // 186 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetFanState": // 187 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetHeaterCoolerState": // 188 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetHeatingCoolingState": // 189 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetHorizontalTiltAngle": // 190 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetHumidifierDehumidifierState": // 191 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetMediaState": // 192 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "2" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetPosition": // 193 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetRelativeHumidity": // 194 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50.0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetSlatState": // 195 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetTemperature": // 196 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "22.2" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetTiltAngle": // 197 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetVerticalTiltAngle": // 198 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TargetVisibilityState": // 199 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TemperatureDisplayUnits": // 200 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ThirdPartyCameraActive": // 201 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TimeUpdate": // 202 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TransmitPower": // 203 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "MaximumTransmitPower": // 204 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TunnelConnectionTimeout": // 205 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "5000" ); else process.stdout.write( `"${ c }"` ); break; } case "TunneledAccessoryAdvertising": // 206 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TunneledAccessoryConnected": // 207 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TunneledAccessoryStateNumber": // 208 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0.0" ); else process.stdout.write( `"${ c }"` ); break; } case "VOCDensity": // 209 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50.0" ); else process.stdout.write( `"${ c }"` ); break; } case "ValveType": // 210 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Version": // 211 { c = readData( device, characteristic ); if ( c == "LockVersion" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "VideoAnalysisActive": // 212 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "Volume": // 213 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "VolumeControlType": // 214 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "VolumeSelector": // 215 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "1" ); else process.stdout.write( `"${ c }"` ); break; } case "WANConfigurationList": // 216 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "WANStatusList": // 217 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "WakeConfiguration": // 218 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "WaterLevel": // 219 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "WiFiCapabilities": // 220 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "50" ); else process.stdout.write( `"${ c }"` ); break; } case "WiFiConfigurationControl": // 221 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "WiFiSatelliteStatus": // 222 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "AssetUpdateReadiness": // 223 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedAssetTypes": // 224 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ConfigurationState": // 225 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "NFCAccessControlPoint": // 226 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "NFCAccessSupportedConfiguration": // 227 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SiriEndpointSessionStatus": // 228 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ThreadControlPoint": // 229 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ThreadNodeCapabilities": // 230 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "ThreadStatus": // 231 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); // To avoid hard coding values, characteristic definitions can be found in: // Cmd4 Github pages -> http://ztalbot2000.github.io/homebridge-cmd4 // Homebridge developer documentation -> https://developers.homebridge.io/#/ break; } case "ThreadOpenThreadVersion": // 232 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } // New Mar 2024 case "AccessCodeControlPoint": // 233 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "AccessCodeSupportedConfiguration": // 234 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "AirPlayEnable": // 235 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CharacteristicValueActiveTransitionCount": // 236 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "CryptoHash": // 237 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "FirmwareUpdateReadiness": // 238 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "FirmwareUpdateStatus": // 239 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "HardwareFinish": // 240 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "MetricsBufferFullState": // 241 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "MultifunctionButton": // 242 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "NetworkAccessViolationControl": // 243 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SelectedDiagnosticsModes": // 244 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SelectedSleepConfiguration": // 245 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SiriEnable": // 246 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SiriEngineVersion": // 247 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SiriLightOnUse": // 248 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SiriListening": // 249 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SiriTouchToUse": // 250 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "StagedFirmwareVersion": // 251 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedMetrics": // 252 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "SupportedFirmwareUpdateConfiguration": // 253 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "TapType": // 254 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } case "Token": // 255 { c = readData( device, characteristic ); if ( c == "" ) process.stdout.write( "0" ); else process.stdout.write( `"${ c }"` ); break; } default: process.stderr.write( `Unknown Characteristic for: ${ io } Device: ${ device } Characteristic: ${ characteristic }` ); process.exit( -1 ); } break; } // End of Switch for "Get" case "Set": { switch( characteristic ) { case "AccessControlLevel": // 0 { writeData( device, characteristic, option ); break; } case "AccessoryFlags": // 1 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "AccessoryIdentifier": // 2 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Active": // 3 { writeData( device, characteristic, option ); break; } case "ActiveIdentifier": // 4 { writeData( device, characteristic, option ); break; } case "ActivityInterval": // 5 { writeData( device, characteristic, option ); break; } case "AdministratorOnlyAccess": // 6 { writeData( device, characteristic, option ); break; } case "AirParticulateDensity": // 7 { writeData( device, characteristic, option ); break; } case "AirParticulateSize": // 8 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "AirQuality": // 9 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "AppMatchingIdentifier": // 10 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "AudioFeedback": // 11 { writeData( device, characteristic, option ); break; } case "BatteryLevel": // 12 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Brightness": // 13 { writeData( device, characteristic, option ); break; } case "ButtonEvent": // 14 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CCAEnergyDetectThreshold": // 15 { writeData( device, characteristic, option ); break; } case "CCASignalDetectThreshold": // 16 { writeData( device, characteristic, option ); break; } case "CameraOperatingModeIndicator": // 17 { writeData( device, characteristic, option ); break; } case "CarbonDioxideDetected": // 18 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CarbonDioxideLevel": // 19 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CarbonDioxidePeakLevel": // 20 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CarbonMonoxideDetected": // 21 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CarbonMonoxideLevel": // 22 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CarbonMonoxidePeakLevel": // 23 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Category": // 24 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CharacteristicValueTransitionControl": // 25 { writeData( device, characteristic, option ); break; } case "ChargingState": // 26 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ClosedCaptions": // 27 { writeData( device, characteristic, option ); break; } case "ColorTemperature": // 28 { writeData( device, characteristic, option ); break; } case "ConfigureBridgedAccessory": // 29 { writeData( device, characteristic, option ); break; } case "ConfigureBridgedAccessoryStatus": // 30 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ConfiguredName": // 31 { writeData( device, characteristic, option ); break; } case "ContactSensorState": // 32 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CoolingThresholdTemperature": // 33 { writeData( device, characteristic, option ); break; } case "CurrentAirPurifierState": // 34 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentAmbientLightLevel": // 35 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentDoorState": // 36 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentFanState": // 37 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentHeaterCoolerState": // 38 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentHeatingCoolingState": // 39 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentHorizontalTiltAngle": // 40 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentHumidifierDehumidifierState": // 41 { writeData( device, characteristic, option ); break; } case "CurrentMediaState": // 42 { writeData( device, characteristic, option ); break; } case "CurrentPosition": // 43 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentRelativeHumidity": // 44 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentSlatState": // 45 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentTemperature": // 46 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentTiltAngle": // 47 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentTime": // 48 { writeData( device, characteristic, option ); break; } case "CurrentTransport": // 49 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentVerticalTiltAngle": // 50 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "CurrentVisibilityState": // 51 { writeData( device, characteristic, option ); break; } case "DataStreamHAPTransport": // 52 { writeData( device, characteristic, option ); break; } case "DataStreamHAPTransportInterrupt": // 53 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "DayoftheWeek": // 54 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "DiagonalFieldOfView": // 55 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "DigitalZoom": // 56 { writeData( device, characteristic, option ); break; } case "DiscoverBridgedAccessories": // 57 { writeData( device, characteristic, option ); break; } case "DiscoveredBridgedAccessories": // 58 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "DisplayOrder": // 59 { writeData( device, characteristic, option ); break; } case "EventRetransmissionMaximum": // 60 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "EventSnapshotsActive": // 61 { writeData( device, characteristic, option ); break; } case "EventTransmissionCounters": // 62 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "FilterChangeIndication": // 63 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "FilterLifeLevel": // 64 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "FirmwareRevision": // 65 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "HardwareRevision": // 66 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "HeartBeat": // 67 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "HeatingThresholdTemperature": // 68 { writeData( device, characteristic, option ); break; } case "HoldPosition": // 69 { writeData( device, characteristic, option ); break; } case "HomeKitCameraActive": // 70 { writeData( device, characteristic, option ); break; } case "Hue": // 71 { writeData( device, characteristic, option ); break; } case "Identifier": // 72 { writeData( device, characteristic, option ); break; } case "Identify": // 73 { writeData( device, characteristic, option ); break; } case "ImageMirroring": // 74 { writeData( device, characteristic, option ); break; } case "ImageRotation": // 75 { writeData( device, characteristic, option ); break; } case "InUse": // 76 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "InputDeviceType": // 77 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "InputSourceType": // 78 { writeData( device, characteristic, option ); break; } case "IsConfigured": // 79 { writeData( device, characteristic, option ); break; } case "LeakDetected": // 80 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "LinkQuality": // 81 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ListPairings": // 82 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "LockControlPoint": // 83 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "LockCurrentState": // 84 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "LockLastKnownAction": // 85 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "LockManagementAutoSecurityTimeout": // 86 { writeData( device, characteristic, option ); break; } case "LockPhysicalControls": // 87 { writeData( device, characteristic, option ); break; } case "LockTargetState": // 88 { writeData( device, characteristic, option ); // Fake it Done writeData(device, "LockCurrentState", option); break; } case "Logs": // 89 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "MACRetransmissionMaximum": // 90 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "MACTransmissionCounters": // 91 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ManagedNetworkEnable": // 92 { writeData( device, characteristic, option ); break; } case "ManuallyDisabled": // 93 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Manufacturer": // 94 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Model": // 95 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "MotionDetected": // 96 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Mute": // 97 { writeData( device, characteristic, option ); break; } case "Name": // 98 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "NetworkAccessViolationControl": // 99 { writeData( device, characteristic, option ); break; } case "NetworkClientProfileControl": // 100 { writeData( device, characteristic, option ); break; } case "NetworkClientStatusControl": // 101 { writeData( device, characteristic, option ); break; } case "NightVision": // 102 { writeData( device, characteristic, option ); break; } case "NitrogenDioxideDensity": // 103 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ObstructionDetected": // 104 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "OccupancyDetected": // 105 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "On": // 106 { writeData( device, characteristic, option ); break; } case "OperatingStateResponse": // 107 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "OpticalZoom": // 108 { writeData( device, characteristic, option ); break; } case "OutletInUse": // 109 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "OzoneDensity": // 110 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "PM10Density": // 111 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "PM2_5Density": // 112 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "PairSetup": // 113 { writeData( device, characteristic, option ); break; } case "PairVerify": // 114 { writeData( device, characteristic, option ); break; } case "PairingFeatures": // 115 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "PairingPairings": // 116 { writeData( device, characteristic, option ); break; } case "PasswordSetting": // 117 { writeData( device, characteristic, option ); break; } case "PeriodicSnapshotsActive": // 118 { writeData( device, characteristic, option ); break; } case "PictureMode": // 119 { writeData( device, characteristic, option ); break; } case "Ping": // 120 { writeData( device, characteristic, option ); break; } case "PositionState": // 121 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "PowerModeSelection": // 122 { writeData( device, characteristic, option ); break; } case "ProductData": // 123 { writeData( device, characteristic, option ); break; } case "ProgramMode": // 124 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ProgrammableSwitchEvent": // 125 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ProgrammableSwitchOutputState": // 126 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Reachable": // 127 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ReceivedSignalStrengthIndication": // 128 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ReceiverSensitivity": // 129 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "RecordingAudioActive": // 130 { writeData( device, characteristic, option ); break; } case "RelativeHumidityDehumidifierThreshold": // 131 { writeData( device, characteristic, option ); break; } case "RelativeHumidityHumidifierThreshold": // 132 { writeData( device, characteristic, option ); break; } case "RelayControlPoint": // 133 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "RelayEnabled": // 134 { writeData( device, characteristic, option ); break; } case "RelayState": // 135 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "RemainingDuration": // 136 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "RemoteKey": // 137 { writeData( device, characteristic, option ); break; } case "ResetFilterIndication": // 138 { writeData( device, characteristic, option ); break; } case "RotationDirection": // 139 { writeData( device, characteristic, option ); break; } case "RotationSpeed": // 140 { writeData( device, characteristic, option ); break; } case "RouterStatus": // 141 { writeData( device, characteristic, option ); break; } case "Saturation": // 142 { writeData( device, characteristic, option ); break; } case "SecuritySystemAlarmType": // 143 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SecuritySystemCurrentState": // 144 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SecuritySystemTargetState": // 145 { writeData( device, characteristic, option ); // Set to done writeData(device,"SecuritySystemCurrentState", option); break; } case "SelectedAudioStreamConfiguration": // 146 { writeData( device, characteristic, option ); break; } case "SelectedCameraRecordingConfiguration": // 147 { writeData( device, characteristic, option ); break; } case "SelectedRTPStreamConfiguration": // 148 { writeData( device, characteristic, option ); break; } case "SerialNumber": // 149 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ServiceLabelIndex": // 150 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ServiceLabelNamespace": // 151 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SetDuration": // 152 { writeData( device, characteristic, option ); break; } case "SetupDataStreamTransport": // 153 { writeData( device, characteristic, option ); break; } case "SetupEndpoints": // 154 { writeData( device, characteristic, option ); break; } case "SetupTransferTransport": // 155 { writeData( device, characteristic, option ); break; } case "SignalToNoiseRatio": // 156 { writeData( device, characteristic, option ); break; } case "SiriInputType": // 157 { writeData( device, characteristic, option ); break; } case "SlatType": // 158 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SleepDiscoveryMode": // 159 { writeData( device, characteristic, option ); break; } case "SleepInterval": // 160 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SmokeDetected": // 161 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SoftwareRevision": // 162 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "StatusActive": // 163 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "StatusFault": // 164 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "StatusJammed": // 165 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "StatusLowBattery": // 166 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "StatusTampered": // 167 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "StreamingStatus": // 168 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SulphurDioxideDensity": // 169 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedAudioRecordingConfiguration": // 170 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedAudioStreamConfiguration": // 171 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedCameraRecordingConfiguration": // 172 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedDataStreamTransportConfiguration": // 173 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedCharacteristicValueTransitionConfiguration": // 174 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedDiagnosticsSnapshot": // 175 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedRTPConfiguration": // 176 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedRouterConfiguration": // 177 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedTransferTransportConfiguration": // 178 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedVideoRecordingConfiguration": // 179 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SupportedVideoStreamConfiguration": // 180 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "SwingMode": // 181 { writeData( device, characteristic, option ); break; } case "TargetAirPurifierState": // 182 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentAirPurifierState", option); break; } case "TargetAirQuality": // 183 { writeData( device, characteristic, option ); // Set to done writeData(device, "AirQuality", option); break; } case "TargetControlList": // 184 { writeData( device, characteristic, option ); break; } case "TargetControlSupportedConfiguration": // 185 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "TargetDoorState": // 186 { writeData( device, characteristic, option ); // Set to Done writeData(device, "CurrentDoorState", option); break; } case "TargetFanState": // 187 { writeData( device, characteristic, option ); // Fake it Done writeData(device, "CurrentFanState", option); break; } case "TargetHeaterCoolerState": // 188 { writeData( device, characteristic, option ); // Fake it Done writeData(device, "CurrentHeaterCoolerState", option); break; } case "TargetHeatingCoolingState": // 189 { writeData( device, characteristic, option ); // Fake it Done writeData(device, "CurrentHeatingCoolingState", option); break; } case "TargetHorizontalTiltAngle": // 190 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentHorizontalTiltAngle", option); break; } case "TargetHumidifierDehumidifierState": // 191 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentHumidifierDehumidifierState", option); break; } case "TargetMediaState": // 192 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentMediaState", option); break; } case "TargetPosition": // 193 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentPosition", option); break; } case "TargetRelativeHumidity": // 194 { writeData( device, characteristic, option ); // Set to done writeData(device, "CurrentRelativeHumidity", option); break; } case "TargetSlatState": // 195 { writeData( device, characteristic, option ); // Set to done writeData(device, "CurrentSlatState", option); break; } case "TargetTemperature": // 196 { writeData( device, characteristic, option ); // Fake it writeData(device, "CurrentTemperature", option); break; } case "TargetTiltAngle": // 197 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentTiltAngle", option); break; } case "TargetVerticalTiltAngle": // 198 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentVerticalTiltAngle", option); break; } case "TargetVisibilityState": // 199 { writeData( device, characteristic, option ); // Set to done writeData(device,"CurrentVisibilityState", option); break; } case "TemperatureDisplayUnits": // 200 { writeData( device, characteristic, option ); break; } case "ThirdPartyCameraActive": // 201 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "TimeUpdate": // 202 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "TransmitPower": // 203 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "MaximumTransmitPower": // 204 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "TunnelConnectionTimeout": // 205 { writeData( device, characteristic, option ); break; } case "TunneledAccessoryAdvertising": // 206 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "TunneledAccessoryConnected": // 207 { writeData( device, characteristic, option ); break; } case "TunneledAccessoryStateNumber": // 208 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "VOCDensity": // 209 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "ValveType": // 210 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "Version": // 211 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "VideoAnalysisActive": // 212 { writeData( device, characteristic, option ); break; } case "Volume": // 213 { writeData( device, characteristic, option ); break; } case "VolumeControlType": // 214 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "VolumeSelector": // 215 { writeData( device, characteristic, option ); break; } case "WANConfigurationList": // 216 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "WANStatusList": // 217 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "WakeConfiguration": // 218 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "WaterLevel": // 219 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "WiFiCapabilities": // 220 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "WiFiConfigurationControl": // 221 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "WiFiSatelliteStatus": // 222 { writeData( device, characteristic, option ); // Not settable in Hap Spec, here for debugging. break; } case "AssetUpdateReadiness": // 223 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SupportedAssetTypes": // 224 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "ConfigurationState": // 225 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "NFCAccessControlPoint": // 226 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "NFCAccessSupportedConfiguration": // 227 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SiriEndpointSessionStatus": // 228 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "ThreadControlPoint": // 229 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "ThreadNodeCapabilities": // 230 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "ThreadStatus": // 231 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "ThreadOpenThreadVersion": // 232 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } // New Mar 2024 case "AccessCodeControlPoint": // 233 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "AccessCodeSupportedConfiguration": // 234 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "AirPlayEnable": // 235 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "CharacteristicValueActiveTransitionCount": // 236 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "CryptoHash": // 237 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "FirmwareUpdateReadiness": // 238 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "FirmwareUpdateStatus": // 239 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "HardwareFinish": // 240 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "MetricsBufferFullState": // 241 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "MultifunctionButton": // 242 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "NetworkAccessViolationControl": // 243 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SelectedDiagnosticsModes": // 244 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SelectedSleepConfiguration": // 245 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SiriEnable": // 246 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SiriEngineVersion": // 247 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SiriLightOnUse": // 248 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SiriListening": // 249 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SiriTouchToUse": // 250 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "StagedFirmwareVersion": // 251 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SupportedMetrics": // 252 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "SupportedFirmwareUpdateConfiguration": // 253 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "TapType": // 254 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } case "Token": // 255 { c = readData( device, characteristic ); writeData( device, characteristic, option ); break; } default: process.stderr.write( `Unknown Characteristic for: ${ io } Device:${ device } Characteristic:${ characteristic }` ); process.exit( -1 ); } break; } // End of Switch Device for "Set" default: process.stderr.write( `Unknown IO ${ io }` ); process.exit( -1 ); } //process.stdout.write( `Say What Device: ${ device } Characteristic: ${ characteristic } Option: ${ option }` ); process.exit( 0 ); ================================================ FILE: Extras/config.json ================================================ { "bridge": { "name": "MAC Test Homebridge", "username": "CC:22:3D:E3:CE:30", "port": 51826, "pin": "555-55-555" }, "platforms" : [ { "platform": "Cmd4", "name": "Cmd4", "debug": false, "allowTLV8": false, "outputConstants": false, "accessories" : [ { "type": "Fanv1", "displayName": "MyV1Fan", "on": "FALSE", "name": "MyV1Fan", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Fan", "displayName": "MyFan", "on": "FALSE", "rotationDirection": "COUNTER_CLOCKWISE", "rotationSpeed": 100, "name": "MyFan", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "outputConstants": true, "stateChangeResponseTime": 3 }, { "type": "Fanv2", "displayName": "MyV2Fan", "active": "INACTIVE", "currentFanState": "IDLE", "targetFanState": "AUTO", "lockPhysicalControls": "CONTROL_LOCK_DISABLED", "rotationDirection": "CLOCKWISE", "rotationSpeed": 100, "swingMode": "SWING_ENABLED", "name": "MyV2Fan", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "GarageDoorOpener", "displayName": "MyGarageDoorOpener", "currentDoorState": "OPEN", "targetDoorState": "OPEN", "obstructionDetected": "FALSE", "lockCurrentState": "UNSECURED", "lockTargetState": "UNSECURED", "name": "MyGarageDoorOpener", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 10, "state_cmd_prefix": "node" }, { "type": "Lightbulb", "displayName": "MyOnOffLight", "on": "FALSE", "name": "MyOnOffLight", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Lightbulb", "displayName": "MyDimmableLight", "on": "FALSE", "brightness": 8, "hue": 8, "saturation": 8, "colorTemperature": 8, "name": "MyDimmableLight", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "LockManagement", "displayName": "MyLockManagement", "version": "1.2.3", "audioFeedback": "FALSE", "lockManagementAutoSecurityTimeout": 20, "administratorOnlyAccess": "FALSE", "lockLastKnownAction": "SECURED_PHYSICALLY_INTERIOR", "currentDoorState": "OPEN", "motionDetected": "FALSE", "name": "MyLockManagement", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "LockMechanism", "displayName": "MyLockMechanism", "lockCurrentState": "UNSECURED", "lockTargetState": "UNSECURED", "name": "MyLockMechanism", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Outlet", "displayName": "MyOutlet", "on": "FALSE", "outletInUse": "FALSE", "name": "MyOutlet", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Switch", "displayName": "MySwitch", "on": "FALSE", "name": "MySwitch", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Thermostat", "displayName": "MyThermostat", "currentHeatingCoolingState": "OFF", "targetHeatingCoolingState": "OFF", "currentTemperature": 22.2, "targetTemperature": 22.2, "temperatureDisplayUnits": "CELSIUS", "currentRelativeHumidity": 60, "targetRelativeHumidity": 60, "coolingThresholdTemperature": 22.2, "heatingThresholdTemperature": 25.2, "name": "MyThermostat", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 10 }, { "type": "AirQualitySensor", "displayName": "MyAirQualitySensor", "airQuality": "EXCELLENT", "ozoneDensity": 50, "nitrogenDioxideDensity": 50, "sulphurDioxideDensity": 50, "pm2_5Density": 50, "pm10Density": 50, "vocDensity": 50, "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyAirQualitySensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "SecuritySystem", "displayName": "MySecuritySystem", "securitySystemCurrentState": "DISARMED", "securitySystemTargetState": "DISARM", "securitySystemAlarmType": 0, "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "name": "MySecuritySystem", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "CarbonMonoxideSensor", "displayName": "MyCOSensor", "carbonMonoxideDetected": "CO_LEVELS_NORMAL", "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "carbonMonoxideLevel": 0, "carbonMonoxidePeakLevel": 0, "name": "MyCOSensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "ContactSensor", "displayName": "MyContactSensor", "contactSensorState": "CONTACT_DETECTED", "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyContactSensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Door", "displayName": "MyDoor", "currentPosition": 0, "targetPosition": 0, "positionState": "STOPPED", "holdPosition": "FALSE", "obstructionDetected": "FALSE", "name": "MyDoor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "HumiditySensor", "displayName": "MyHumiditySensor", "currentRelativeHumidity": 1, "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyHumiditySensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "LeakSensor", "displayName": "MyLeakSensor", "leakDetected": "LEAK_NOT_DETECTED", "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyLeakSensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "LightSensor", "displayName": "MyLightSensor", "currentAmbientLightLevel": 1, "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyLightSensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "MotionSensor", "displayName": "MyMotionSensor", "motionDetected": "FALSE", "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyMotionSensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "OccupancySensor", "displayName": "MyOccupancySensor", "occupancyDetected": "OCCUPANCY_NOT_DETECTED", "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyOccupancySensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "SmokeSensor", "displayName": "MySmokeSensor", "smokeDetected": "SMOKE_NOT_DETECTED", "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MySmokeSensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "StatefulProgrammableSwitch", "displayName": "MyStatefulProgrammableSwitch", "programmableSwitchEvent": "SINGLE_PRESS", "programmableSwitchOutputState": 0, "name": "MyStatefulProgrammableSwitch", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "StatelessProgrammableSwitch", "displayName": "MyStatelessProgrammableSwitch", "programmableSwitchEvent": "SINGLE_PRESS", "name": "MyStatelessProgrammableSwitch", "serviceLabelIndex": 1, "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "TemperatureSensor", "displayName": "MyTemperatureSensor", "currentTemperature": 22.2, "currentRelativeHumidity": 20, "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyTemperatureSensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Window", "displayName": "MyWindow", "currentPosition": 0, "targetPosition": 0, "positionState": "STOPPED", "holdPosition": "FALSE", "obstructionDetected": "FALSE", "name": "MyWindow", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "WindowCovering", "displayName": "MyWindowCovering", "currentPosition": 0, "targetPosition": 0, "positionState": "STOPPED", "holdPosition": "FALSE", "currentHorizontalTiltAngle":0, "targetHorizontalTiltAngle": 0, "currentVerticalTiltAngle": 0, "targetVerticalTiltAngle": 0, "obstructionDetected": "FALSE", "name": "MyWindowCovering", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Battery", "displayName": "MyBattery", "batteryLevel": 50, "chargingState": "NOT_CHARGING", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "name": "MyBatteryService", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "CarbonDioxideSensor", "displayName": "MyCO2Sensor", "carbonDioxideDetected": "CO2_LEVELS_NORMAL", "statusActive": "TRUE", "statusFault": "NO_FAULT", "statusTampered": "NOT_TAMPERED", "statusLowBattery": "BATTERY_LEVEL_NORMAL", "carbonDioxideLevel": 0, "carbonDioxidePeakLevel": 0, "name": "MyCO2Sensor", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "interval": 540, "stateChangeResponseTime": 3 }, { "type": "Microphone", "displayName": "MyMicrophone", "mute": "FALSE", "volume": 5, "name": "MyMicrophone", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Speaker", "displayName": "MySpeaker", "mute": "FALSE", "volume": 5, "name": "MySpeaker", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "DoorBell", "displayName": "MyDoorBell", "programmableSwitchEvent": "SINGLE_PRESS", "brightness": 8, "volume": 5, "name": "MyDoorBell", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Slats", "displayName": "MySlats", "slatType": "HORIZONTAL", "currentSlatState": "FIXED", "name": "MySlat", "currentTiltAngle": 0, "targetTiltAngle": 0, "swingMode": "SWING_ENABLED", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "interval": 540, "stateChangeResponseTime": 3 }, { "type": "FilterMaintenance", "displayName": "MyFilterMaintenance", "filterChangeIndication": "FILTER_OK", "filterLifeLevel": 50, "resetFilterIndication": 1, "name": "MyFilterMaintenance", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "AirPurifier", "displayName": "MyAirPurifier", "active": "ACTIVE", "currentAirPurifierState": "INACTIVE", "targetAirPurifierState": "MANUAL", "lockPhysicalControls": "CONTROL_LOCK_DISABLED", "name": "MyAirPurifier", "swingMode": "SWING_DISABLED", "rotationSpeed": 0, "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "ServiceLabel", "displayName": "MyServiceLabel", "serviceLabelNamespace": "DOTS", "name": "MyServiceLabel", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Television", "displayName": "Tv_Combo", "publishExternally": true, "category": "TELEVISION", "active": "ACTIVE", "activeIdentifier": 1, "configuredName": "TV_Combo", "sleepDiscoveryMode": "ALWAYS_DISCOVERABLE", "linkedTypes": [ { "type": "InputSource", "displayName": "HDMI1", "configuredName": "HDMI1", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "identifier": 1, "targetVisibilityState": "SHOWN", "name": "HDMI1", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345" }, { "type": "InputSource", "displayName": "HDMI2", "configuredName": "HDMI2", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "identifier": 2, "targetVisibilityState": "SHOWN", "name": "HDMI2", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345" }, { "type": "InputSource", "displayName": "HDMI3", "configuredName": "HDMI3", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "identifier": 3, "targetVisibilityState": "SHOWN", "name": "HDMI3", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345" }, { "type": "InputSource", "displayName": "HDMI4", "configuredName": "HDMI4", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "identifier": 4, "targetVisibilityState": "SHOWN", "name": "HDMI4", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345" }, { "type": "InputSource", "displayName": "Netflix", "configuredName": "Netflix", "currentVisibilityState": "SHOWN", "inputSourceType": "APPLICATION", "isConfigured": "CONFIGURED", "identifier": 5, "targetVisibilityState": "SHOWN", "name": "Netflix", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345" }, { "type": "TelevisionSpeaker", "displayName": "TVSpeaker", "mute": "FALSE", "active": "ACTIVE", "volume": 10, "volumeControlType": "ABSOLUTE", "volumeSelector": "INCREMENT", "name": "TVSpeaker", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345" } ], "brightness": 8, "closedCaptions": "DISABLED", "currentMediaState": "STOP", "targetMediaState": "STOP", "pictureMode": "STANDARD", "remoteKey": "SELECT", "manufacturer": "Sony", "model": "Android TV", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "TelevisionSpeaker", "displayName": "MyTVSpeaker", "mute": "FALSE", "active": "ACTIVE", "volume": 5, "volumeControlType": "ABSOLUTE", "volumeSelector": "INCREMENT", "name": "MyTVSpeaker", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "InputSource", "displayName": "MyInputSource", "configuredName": "MyInputSource", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "currentVisibilityState": "SHOWN", "inputDeviceType": "TV", "targetVisibilityState": "SHOWN", "name": "MyInputSource", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "IrrigationSystem", "displayName": "MyIrrigationSystem", "active": "ACTIVE", "programMode": "PROGRAM_SCHEDULED", "inUse": "IN_USE", "remainingDuration": 60, "statusFault": "NO_FAULT", "name": "MyIrrigationSystem", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "Faucet", "displayName": "MyFaucet", "active": "ACTIVE", "statusFault": "NO_FAULT", "name": "MyFaucet", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "CloudRelay", "displayName": "MyCloudRelay", "relayEnabled": "FALSE", "relayState": 0, "stateChangeResponseTime": 10 }, { "type": "Valve", "displayName": "MyValve", "active": "ACTIVE", "inUse": "NOT_IN_USE", "valveType": "GENERIC_VALVE", "setDuration": 0, "remainingDuration": 0, "isConfigured": "CONFIGURED", "serviceLabelIndex": 1, "statusFault": "NO_FAULT", "name": "MyValve", "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "HeaterCooler", "displayName": "MyHeaterCooler", "active": "ACTIVE", "currentHeaterCoolerState": "HEATING", "targetHeaterCoolerState": "AUTO", "currentTemperature": 37.0, "lockPhysicalControls": "CONTROL_LOCK_DISABLED", "name": "MyHeaterCooler", "swingMode": "SWING_ENABLED", "coolingThresholdTemperature": 22.2, "heatingThresholdTemperature": 25.2, "temperatureDisplayUnits": "CELSIUS", "rotationSpeed": 100, "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "HumidifierDehumidifier", "displayName": "MyHumidifierDehumidifier", "currentRelativeHumidity": 60, "currentHumidifierDehumidifierState": "HUMIDIFYING", "targetHumidifierDehumidifierState": "HUMIDIFIER", "active": "ACTIVE", "lockPhysicalControls": "CONTROL_LOCK_DISABLED", "name": "MyHumidifierDehumidifier", "swingMode": "SWING_DISABLED", "waterLevel": 0, "relativeHumidityDehumidifierThreshold": 0, "relativeHumidityHumidifierThreshold": 0, "rotationSpeed": 0, "manufacturer": "Somebody", "model": "Anything", "serialNumber": "12345", "stateChangeResponseTime": 3 }, { "type": "AccessControl", "displayName": "MyAccessControl", "accessControlLevel": 0, "stateChangeResponseTime": 3 }, { "type": "CameraOperatingMode", "displayName": "MyCameraOperatingMode", "eventSnapshotsActive": "DISABLE", "homeKitCameraActive": "OFF", "manuallyDisabled": "DISABLED", "nightVision": "FALSE", "thirdPartyCameraActive": "OFF", "periodicSnapshotsActive": "DISABLE" }, { "type": "SmartSpeaker", "displayName": "MySmartSpeaker", "currentMediaState": "STOP", "targetMediaState": "STOP", "name": "MySmartSpeaker", "configuredName": "MySmartSpeaker", "volume": 5, "mute": "FALSE" }, { "type": "TargetControl", "displayName": "MyTargetControl", "activeIdentifier": 1183, "active": "ACTIVE" }, { "type": "WiFiSatellite", "displayName": "MyWiFiSatellite", "wifiSatelliteStatus": "NOT_CONNECTED" } ] } ] } ================================================ FILE: Extras/config.min.json ================================================ { "bridge": { "name": "MAC Test Homebridge", "username": "CC:22:3D:E3:CE:30", "port": 51826, "pin": "555-55-555" }, "platforms" : [ { "platform": "Cmd4", "name": "Cmd4", "debug": false, // Output debug logs "allowTLV8": false, // Allow TLV8 Characteristics // These cannot be described easily // in JSON format. They are usually // binary formats. // The default is false. "outputConstants": false, // Instead of numeric values, output // the value in string form. // The default is false. "accessories" : [ { "type": "Fanv1", "displayName": "MyV1Fan", // Not a characteristic, but a function parameter // Required characteristics "on": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics "name": "MyV1Fan", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) // Paramaters to state_cmd are: // Get // Set // "Note": There is no need for an on_cmd or off_cmd // with this kind of detail. //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Fan", "displayName": "MyFan", // Not a characteristic, but a function parameter // Required Characteristics "on": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics "rotationDirection": "COUNTER_CLOCKWISE", // 0 - "CLOCKWISE" // 1 - "COUNTER_CLOCKWISE" "rotationSpeed": 100, // Range: 0-100, Step: 1 "name": "MyFan", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "outputConstants": true, // For this accessory, output constants // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "rotationDirection", "interval": 40, "timeout": 8000}, // {"characteristic": "on", "interval": 40, "timeout": 8000} //], "stateChangeResponseTime": 3 // Default (Seconds) // Paramaters to state_cmd are: // Get // Set // "Note": There is no need for an on_cmd or off_cmd // with this kind of detail. //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Fanv2", "displayName": "MyV2Fan", // Not a characteristic, but a function parameter // Required Characteristics "active": "INACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" // Optional Characteristics "currentFanState": "IDLE", // 0 - "INACTIVE" // 1 - "IDLE" // 2 - "BLOWING_AIR" "targetFanState": "AUTO", // 0 - "MANUAL" // 1 - "AUTO" "lockPhysicalControls": "CONTROL_LOCK_DISABLED", // 0 - "CONTROL_LOCK_DISABLED" // 1 - "CONTROL_LOCK_ENABLED" "rotationDirection": "CLOCKWISE", // 0 - "COUNTER_CLOCKWISE" // 1 - "CLOCKWISE" "rotationSpeed": 100, // Range: 0-100, Step: 1 "swingMode": "SWING_ENABLED", // 0 - "SWING_DISABLED", // 1 - "SWING_ENABLED" "name": "MyV2Fan", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "GarageDoorOpener", "displayName": "MyGarageDoorOpener", // Not a characteristic, but a function parameter // Required Characteristics "currentDoorState": "OPEN", // 0 - "OPEN" // 1 - "CLOSED" // 2 - "OPENING" // 3 - "CLOSING" // 4 - "STOPPED" "targetDoorState": "OPEN", // 0 - "OPEN" // 1 - "CLOSED" "obstructionDetected": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics "lockCurrentState": "UNSECURED", // 0 - "UNSECURED" // 1 - "SECURED" // 2 - "JAMMED" // 3 - "UNKNOWN" "lockTargetState": "UNSECURED", // 0 - "UNSECURED" // 1 - "SECURED" "name": "MyGarageDoorOpener", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // Define the characteristic, its Interval and Timeout // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. // "polling": [ // {"characteristic": "currentDoorState", "interval": 540, "timeout": 8000}, // {"characteristic": "obstructionDetected", "interval": 540, "timeout": 8000}, // {"characteristic": "lockCurrentState", "interval": 540, "timeout": 8000} // ], "stateChangeResponseTime": 10, // Default (Seconds) // This is just a dumb example of a prefix "state_cmd_prefix": "node" //"state_cmd": ".homebridge/< yourScriptHere >" }, { "type": "Lightbulb", "displayName": "MyOnOffLight", // Not a characteristic, but a function parameter // Required Characteristics "on": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics // Not Defining brightness determines that it is an On/Off Light // "Brightness": // "Hue": // "Saturation": "name": "MyOnOffLight", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // "ColorTemperature": // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Lightbulb", "displayName": "MyDimmableLight", // Not a characteristic, but a function parameter // Required "on": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics // Defining brightness determines that it is dimmmable "brightness": 8, // Range: 0-100, Step: 1 "hue": 8, // Range: 0-360, Step: 1 "saturation": 8, // Range: 0-100, Step: 1 "colorTemperature": 8, // Range: 140-500, Step: 1 "name": "MyDimmableLight", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "LockManagement", "displayName": "MyLockManagement", // Not a characteristic, but a function parameter // Required Characteristics // All TLV8 Characteristics are removed by default, // unless 'allowTLV8' is set, at your own peril. // Setting most will cause Homebridge with HomeKit or Eve. // to not allow you to connect. // "lockControlPoint": 0, // Format TLV8 "version": "1.2.3", // Format: String // Optional Characteristics // All TLV8 Characteristics are removed by default, // unless 'allowTLV8' is set, at your own peril. // Setting most will cause Homebridge with HomeKit or Eve. // to not allow you to connect. // "logs": 0, // Format: TLV8 "audioFeedback": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "lockManagementAutoSecurityTimeout": 20, // Format: UINT32 // "Units": Seconds "administratorOnlyAccess": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "lockLastKnownAction": "SECURED_PHYSICALLY_INTERIOR", // 0 - "SECURED_PHYSICALLY_INTERIOR" // 1 - "UNSECURED_PHYSICALLY_INTERIOR" // 2 - "SECURED_PHYSICALLY_EXTERIOR" // 3 - "UNSECURED_PHYSICALLY_EXTERIOR" // 4 - "SECURED_BY_KEYPAD" // 5 - "UNSECURED_BY_KEYPAD" // 6 - "SECURED_REMOTELY" // 7 - "UNSECURED_REMOTELY" // 8 - "SECURED_BY_AUTO_SECURE_TIMEOUT" "currentDoorState": "OPEN", // 0 - "OPEN" // 1 - "CLOSED" // 2 - "OPENING" // 3 - "CLOSING" // 4 - "STOPPED" "motionDetected": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "name": "MyLockManagement", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "LockMechanism", "displayName": "MyLockMechanism", // Not a characteristic, but a function parameter // Required Characteristics "lockCurrentState": "UNSECURED", // 0 - "UNSECURED" // 1 - "SECURED" // 2 - "JAMMED" // 3 - "UNKNOWN" "lockTargetState": "UNSECURED", // 0 - "UNSECURED" // 1 - "SECURED" "name": "MyLockMechanism", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Outlet", "displayName": "MyOutlet", // Not a characteristic, but a function parameter // Required Characteristics "on": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "outletInUse": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics "name": "MyOutlet", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Switch", "displayName": "MySwitch", // Not a characteristic, but a function parameter // Required Characteristics "on": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics "name": "MySwitch", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Thermostat", "displayName": "MyThermostat", // Not a characteristic, but a function parameter // Required Characteristics "currentHeatingCoolingState": "OFF", // 0 - "OFF" // 1 - "HEAT" // 2 - "COOL" "targetHeatingCoolingState": "OFF", // 0 - "OFF" // 1 - "HEAT" // 2 - "COOL" // 3 - "AUTO" "currentTemperature": 22.2, // Range: 0-100, Step: 0.1 // "Units": CELCIUS "targetTemperature": 22.2, // Range: 10-38, Step: 0.1 // "Units": CELCIUS "temperatureDisplayUnits": "CELSIUS", // 0 - "CELSIUS" // 1 - "FAHRENHEIT" // Optional Characteristics "currentRelativeHumidity": 60, // Range: 0-100, Step: 1 // Format: float // Units: Percentage "targetRelativeHumidity": 60, // Range: 0-100, Step: 1 // Format: float // Units: Percentage "coolingThresholdTemperature": 22.2, // Range 10-35, Step: 0.1 // Units: CELCIUS "heatingThresholdTemperature": 25.2, // Range 0-25, Step: 0.1 // Units: CELCIUS "name": "MyThermostat", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific - Interval is in second, Timeout in msec // - Notice Two characteristics being polled !!! // at different intervals. // - Also Required as fakegato option below uses them. // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "currentHeatingCoolingState", "interval": 540, "timeout": 8000}, // {"characteristic": "currentTemperature", "interval": 60, "timeout": 8000}, // {"characteristic": "targetTemperature", "interval": 60, "timeout": 8000} //], // Cmd4 Specific "stateChangeResponseTime": 10 // (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >", // Cmd4 fakegato lets you specify what type of logging and // the characteristics to be logged. // i.e. thermo and tracking currentTemperature and // targetTemperature. // See https://github.com/simont77/fakegato-history // for full description of fields. // You must also define them for polling, or the value would never change. // fakegato requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"fakegato": {"eve": "thermo", // "currentTemp": "CurrentTemperature", // "setTemp": "TargetTemperature", // "valvePosition": 0, // "storage": "fs", // "storagePath": ".homebridge/FakegatoStorage", // "folder": "folderName", // "keyPath": "/place/to/store/my/keys/" // } }, { "type": "AirQualitySensor", "displayName": "MyAirQualitySensor", // Not a characteristic, but a function parameter // Required Characteristics "airQuality": "EXCELLENT", // 0 - "UNKNOWN" // 1 - "EXCELLENT" // 2 - "GOOD" // 3 - "FAIR" // 4 - "INFERIOR" // 5 - "POOR" "ozoneDensity": 50, // Range 0-1000, Step 1 "nitrogenDioxideDensity": 50, // Range 0-1000, Step 1 "sulphurDioxideDensity": 50, // Range 0-1000, Step 1 "pm2_5Density": 50, // Range 0-1000, Step 1 // Optional Characteristics "pm10Density": 50, // Range 0-1000, Step 1 "vocDensity": 50, // Range 0-1000, Step 1 "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyAirQualitySensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "SecuritySystem", "displayName": "MySecuritySystem", // Not a characteristic, but a function parameter // Required Characteristics "securitySystemCurrentState": "DISARMED", // 0 - "STAY_ARM" // 1 - "AWAY_ARM" // 2 - "NIGHT_ARM" // 3 - "DISARMED" // 4 - "ALARM_TRIGGERED" "securitySystemTargetState": "DISARM", // 0 - "STAY_ARM" // 1 - "AWAY_ARM" // 2 - "NIGHT_ARM" // 3 - "DISARM" // Optional Characteristics "securitySystemAlarmType": 0, // Range: 0-1, Step 1 // Meaning Unknown "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "name": "MySecuritySystem", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "CarbonMonoxideSensor", "displayName": "MyCOSensor", // Not a characteristic, but a function parameter // Required Characteristics "carbonMonoxideDetected": "CO_LEVELS_NORMAL", // 0 - "CO_LEVELS_NORMAL" // 1 - "CO_LEVELS_ABNORMAL" // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "carbonMonoxideLevel": 0, // Range: 0-100, // Format: float "carbonMonoxidePeakLevel": 0, // Range: 0-100, // Format: float "name": "MyCOSensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "ContactSensor", "displayName": "MyContactSensor", // Not a characteristic, but a function parameter // Required Characteristics "contactSensorState": "CONTACT_DETECTED", // 0 - "CONTACT_DETECTED" // 1 - "CONTACT_NOT_DETECTED" // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyContactSensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Door", "displayName": "MyDoor", // Not a characteristic, but a function parameter // Required Characteristics "currentPosition": 0, // Range: 0 - 100, Step 1 "targetPosition": 0, // Range: 0 - 100, Step 1 "positionState": "STOPPED", // 0 - "DECREASING" // 1 - "INCREASING" // 2 - "STOPPED" // Optional Characteristics "holdPosition": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "obstructionDetected": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "name": "MyDoor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // Required as fakegato option below uses them. // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "currentPosition", "interval": 80, "timeout": 8000}, // {"characteristic": "obstructionDetected", "interval": 50, "timeout": 8000} //], // Cmd4 fakegato lets you specify what type of logging and // the characteristics to be logged. // i.e. door and tracking statusActive // You must also define them for polling, or the value would never change. // fakegato requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"fakegato": {"eve": "door", // "status": "currentPosition", // "storage": "fs", // "storagePath": ".homebridge/FakegatoStorage", // "folder": "folderName", // "keyPath": "/place/to/store/my/keys/" // }, // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "HumiditySensor", "displayName": "MyHumiditySensor", // Not a characteristic, but a function parameter // Required Characteristics "currentRelativeHumidity": 1, // Range: 0-100, Step: 1 // Format: float // Units: Percentage // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyHumiditySensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >", // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // Required as fakegato option below uses them. // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "currentRelativeHumidity", "interval": 50, "timeout": 8000} //], // Cmd4 fakegato lets you specify what type of logging and // the characteristics to be logged. // i.e. thermo and tracking currentTemperature and // targetTemperature. // You must also define them for polling, or the value would never change. // fakegato requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"fakegato": {"eve": "room", // "temp": 0, // "humidity": "currentRelativeHumidity", // "ppm": 0, // "storage": "fs", // "storagePath": ".homebridge/FakegatoStorage", // "folder": "folderName", // "keyPath": "/place/to/store/my/keys/" // } }, { "type": "LeakSensor", "displayName": "MyLeakSensor", // Not a characteristic, but a function parameter // Required Characteristics "leakDetected": "LEAK_NOT_DETECTED", // 0 - "LEAK_NOT_DETECTED" // 1 - "LEAK_DETECTED" // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyLeakSensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "LightSensor", "displayName": "MyLightSensor", // Not a characteristic, but a function parameter // Required Characteristics "currentAmbientLightLevel": 1, // Range: 0.0001-100000 // Format: float // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyLightSensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "MotionSensor", "displayName": "MyMotionSensor", // Not a characteristic, but a function parameter // Required Characteristics "motionDetected": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyMotionSensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "OccupancySensor", "displayName": "MyOccupancySensor", // Not a characteristic, but a function parameter // Required Characteristics "occupancyDetected": "OCCUPANCY_NOT_DETECTED", // 0 - "OCCUPANCY_NOT_DETECTED" // 1 - "OCCUPANCY_DETECTED" // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyOccupancySensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "SmokeSensor", "displayName": "MySmokeSensor", // Not a characteristic, but a function parameter // Required Characteristics "smokeDetected": "SMOKE_NOT_DETECTED", // 0 - "SMOKE_NOT_DETECTED" // 1 - "SMOKE_DETECTED" // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MySmokeSensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "StatefulProgrammableSwitch", "displayName": "MyStatefulProgrammableSwitch", // Not a characteristic, but a function parameter // Required Characteristics "programmableSwitchEvent": "SINGLE_PRESS", // 0 - "SINGLE_PRESS" // 1 - "DOUBLE_PRESS" // 2 - "LONG_PRESS" "programmableSwitchOutputState": 0, // Range: 0-1, Step 1 // "Meaning": ??? // Optional Characteristics "name": "MyStatefulProgrammableSwitch", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "StatelessProgrammableSwitch", "displayName": "MyStatelessProgrammableSwitch", // Not a characteristic, but a function parameter // Required Characteristics "programmableSwitchEvent": "SINGLE_PRESS", // 0 - "SINGLE_PRESS" // 1 - "DOUBLE_PRESS" // 2 - "LONG_PRESS" // Optional Characteristics "name": "MyStatelessProgrammableSwitch", "serviceLabelIndex": 1, // Range: 1-255, Step: 1 "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "TemperatureSensor", "displayName": "MyTemperatureSensor", // Not a characteristic, but a function parameter // Required Characteristics "currentTemperature": 22.2, // Range: 0-100, Step: 0.1 // Format: float // "Units": CELCIUS // This characteristic is not available on a temperature sensor // but configurable in homebridge-cmd4. How Homekit behaves is // undeterministic. Eve however is more flexible. "currentRelativeHumidity": 20, // Range: 0-100, Step: 1 // Format: float // Units: Percentage // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "name": "MyTemperatureSensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // Required as fakegato option below uses them. // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": //[ // {"characteristic": "currentTemperature", "interval": 50, "timeout": 8000}, // {"characteristic": "currentRelativeHumidity", "interval": 40, "timeout": 8000} //], // Cmd4 Specific (Fakegate ) // You must also define them for polling, or the value would never change. // fakegato requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"fakegato": {"eve": "room", // "temp": "currentTemperature", // "humidity": "currentRelativeHumidity", // "storage": "fs", // "storagePath": ".homebridge/FakegatoStorage", // "folder": "folderName", // "keyPath": "/place/to/store/my/keys/" // }, // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Window", "displayName": "MyWindow", // Not a characteristic, but a function parameter // Required Characteristics "currentPosition": 0, // Range: 0 - 100, Step 1 "targetPosition": 0, // Range: 0 - 100, Step 1 "positionState": "STOPPED", // 0 - "DECREASING" // 1 - "INCREASING" // 2 - "STOPPED" // Optional Characteristics "holdPosition": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "obstructionDetected": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "name": "MyWindow", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "WindowCovering", "displayName": "MyWindowCovering", // Not a characteristic, but a function parameter // Required Characteristics "currentPosition": 0, // Range: 0 - 100, Step 1 "targetPosition": 0, // Range: 0 - 100, Step 1 "positionState": "STOPPED", // 0 - "DECREASING" // 1 - "INCREASING" // 2 - "STOPPED" // Optional Characteristics "holdPosition": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "currentHorizontalTiltAngle":0, // Range: -90 to 90, Step 1 // "Units": ARC_DEGREE "targetHorizontalTiltAngle": 0, // Range: -90 to 90, Step 1 // "Units": ARC_DEGREE "currentVerticalTiltAngle": 0, // Range: -90 to 90, Step 1 // "Units": ARC_DEGREE "targetVerticalTiltAngle": 0, // Range: -90 to 90, Step 1 // "Units": ARC_DEGREE "obstructionDetected": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "name": "MyWindowCovering", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Battery", // Was BatteryService "displayName": "MyBattery", // Not a characteristic, but a function parameter // Required Characteristics "batteryLevel": 50, // Range: 0 - 100, Step 1 "chargingState": "NOT_CHARGING", // 0 - "NOT_CHARGING" // 1 - "CHARGING" // 2 - "NOT_CHARGEABLE" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" // Optional Characteristics "name": "MyBatteryService", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "CarbonDioxideSensor", "displayName": "MyCO2Sensor", // Required Characteristics "carbonDioxideDetected": "CO2_LEVELS_NORMAL", // 0 - "CO2_LEVELS_NORMAL" // 1 - "CO2_LEVELS_ABNORMAL" // Optional Characteristics "statusActive": "TRUE", // 0 - "FALSE" // 1 - "TRUE" "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "statusTampered": "NOT_TAMPERED", // 0 - "NOT_TAMPERED" // 1 - "TAMPERED" "statusLowBattery": "BATTERY_LEVEL_NORMAL", // 0 - "BATTERY_LEVEL_NORMAL" // 1 - "BATTERY_LEVEL_LOW" "carbonDioxideLevel": 0, // Range: 0-100000 // "Format": float "carbonDioxidePeakLevel": 0, // Range: 0-100000 // "Format": float "name": "MyCO2Sensor", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": false, // Default is false. "interval": 540, // Interval is in seconds. // Default is 10 // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, // Not Possible in this implementation // { // "type": "CameraRTPStreamManagement", // "name": "MyCameraRTPStreamManagement", // // // Cmd4 Specific // "stateChangeResponseTime": 1, // Default (Seconds) // "state_cmd": "node .homebridge/< yourScriptHere >" // }, { "type": "Microphone", "displayName": "MyMicrophone", // Not a characteristic, but a function parameter // Required Characteristics "mute": "FALSE", // 0 - "FALSE" (Off) // 1 - "TRUE" (On) // Optional Characteristics "volume": 5, // Range: 0-100, Step 1 "name": "MyMicrophone", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) // "state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Speaker", "displayName": "MySpeaker", // Required Characteristics "mute": "FALSE", // 0 - "FALSE" (Off) // 1 - "TRUE" (On) // Optional Characteristics "volume": 5, // Range: 0-100, Step 1 "name": "MySpeaker", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "DoorBell", "displayName": "MyDoorBell", // Not a characteristic, but a function parameter // Required Characteristics "programmableSwitchEvent": "SINGLE_PRESS", // 0 - "SINGLE_PRESS" // 1 - "DOUBLE_PRESS" // 2 - "LONG_PRESS" // Optional Characteristics "brightness": 8, // Range: 0-100, Step: 1 "volume": 5, // Range: 0-100, Step 1 "name": "MyDoorBell", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Slats", "displayName": "MySlats", // Not a characteristic, but a function parameter // Required Characteristics "slatType": "HORIZONTAL", // 0 - "HORIZONTAL" // 1 - VERTICAL" "currentSlatState": "FIXED", // 0 - "FIXED" // 1 - "JAMMED" // 2 - "SWINGING" // Optional Characteristics "name": "MySlat", "currentTiltAngle": 0, // Range: -90 to 90, Step 1 // "Units": ARC_DEGREE "targetTiltAngle": 0, // Range: -90 to 90, Step 1 // "Units": ARC_DEGREE "swingMode": "SWING_ENABLED", // 0 - "SWING_DISABLED" // 1 - "SWING_ENABLED" "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": false, // Default is false. "interval": 540, // Interval is in seconds. Default is 10 // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "FilterMaintenance", "displayName": "MyFilterMaintenance", // Not a characteristic, but a function parameter // Required Characteristics "filterChangeIndication": "FILTER_OK", // 0 - "FILTER_OK" // 1 - "CHANGE_FILTER" // Optional Characteristics "filterLifeLevel": 50, // Range: 0-100 // "Format": float "resetFilterIndication": 1, // Range: 1 - ??? "name": "MyFilterMaintenance", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "AirPurifier", "displayName": "MyAirPurifier", // Not a characteristic, but a function parameter // Required Characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" "currentAirPurifierState": "INACTIVE", // 0 - "INACTIVE" // 1 - "IDLE" // 2 - "PURIFYING_AIR" "targetAirPurifierState": "MANUAL", // 0 - "MANUAL" // 1 - "AUTO" // Optional Characteristics "lockPhysicalControls": "CONTROL_LOCK_DISABLED", // 0 - "CONTROL_LOCK_DISABLED" // 1 - "CONTROL_LOCK_ENABLED" "name": "MyAirPurifier", "swingMode": "SWING_DISABLED", // 0 - "SWING_DISABLED" // 1 - "SWING_ENABLED" "rotationSpeed": 0, // Range: 0 - 100, Step 1 "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "ServiceLabel", "displayName": "MyServiceLabel", // Not a characteristic, but a function parameter // Required Characteristics "serviceLabelNamespace": "DOTS", // 0 - "DOTS" // 1 - "ARABIC_NUMERALS" // Optional Characteristics "name": "MyServiceLabel", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Television", "displayName": "Tv_Combo", // Not a characteristic, but a function parameter // Televisions should be published externally to get the icon "publishExternally": true, "category": "TELEVISION", // The hint to Homekit of which icon to use. // Required characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" "activeIdentifier": 1, // This is which inputSource is active // We gave HDMI2 the identifier of 1. // Format: UINT32 "configuredName": "TV_Combo", "sleepDiscoveryMode": "ALWAYS_DISCOVERABLE", // 0 - "NOT_DISCOVERABLE" // 1 - "ALWAYS_DISCOVERABLE" "linkedTypes": [ { "type": "InputSource", "displayName": "HDMI1", // Not a characteristic, but a function parameter // Required Characteristics "configuredName": "HDMI1", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", // Optional Characteristics "identifier": 1, // Selector for activeIdentifier // Format: UINT32 //"InputDeviceType": "TUNER", // 0 - "OTHER" // 1 - "TV" // 2 - "RECORDING" // 3 - "TUNER" // 4 - "PLAYBACK" // 5 - "AUDIO_SYSTEM" // 6 - "UNKNOWN_6" (Added in IOS 14) "targetVisibilityState": "SHOWN", // 0 - "SHOWN" // 1 - "HIDDEN" // 2 - "STOP" "name": "HDMI1", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345" // Available for all accessories }, { "type": "InputSource", "displayName": "HDMI2", // Not a characteristic, but a function parameter // Required Characteristics "configuredName": "HDMI2", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", // Optional Characteristics "identifier": 2, // Selector for activeIdentifier // Format: UINT32 //"inputDeviceType": // "TUNER", // 0 - "OTHER" // 1 - "TV" // 2 - "RECORDING" // 3 - "TUNER" // 4 - "PLAYBACK" // 5 - "AUDIO_SYSTEM" "targetVisibilityState": "SHOWN", // 0 - "SHOWN" // 1 - "HIDDEN" // 2 - "STOP" "name": "HDMI2", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345" // Available for all accessories }, { "type": "InputSource", "displayName": "HDMI3", // Not a characteristic, but a function parameter // Required Characteristics "configuredName": "HDMI3", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", // Optional Characteristics "identifier": 3, // Selector for activeIdentifier // Format: UINT32 //"InputDeviceType": // "TUNER", // 0 - "OTHER" // 1 - "TV" // 2 - "RECORDING" // 3 - "TUNER" // 4 - "PLAYBACK" // 5 - "AUDIO_SYSTEM" "targetVisibilityState": "SHOWN", // 0 - "SHOWN" // 1 - "HIDDEN" // 2 - "STOP" "name": "HDMI3", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345" // Available for all accessories }, { "type": "InputSource", "displayName": "HDMI4", // Not a characteristic, but a function parameter // Required Characteristics "configuredName": "HDMI4", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", // Optional Characteristics "identifier": 4, // Selector for activeIdentifier // Format: UINT32 //"inputDeviceType": "TUNER", // 0 - "OTHER" // 1 - "TV" // 2 - "RECORDING" // 3 - "TUNER" // 4 - "PLAYBACK" // 5 - "AUDIO_SYSTEM" "targetVisibilityState": "SHOWN", // 0 - "SHOWN" // 1 - "HIDDEN" // 2 - "STOP" "name": "HDMI4", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345" // Available for all accessories }, { "type": "InputSource", "displayName": "Netflix", // Not a characteristic, but a function parameter // Required Characteristics "configuredName": "Netflix", "currentVisibilityState": "SHOWN", "inputSourceType": "APPLICATION", "isConfigured": "CONFIGURED", // Optional Characteristics "identifier": 5, // Selector for activeIdentifier // Format: UINT32 // "inputDeviceType": "OTHER", // 0 - "OTHER" // 1 - "TV" // 2 - "RECORDING" // 3 - "TUNER" // 4 - "PLAYBACK" // 5 - "AUDIO_SYSTEM" "targetVisibilityState": "SHOWN", // 0 - "SHOWN" // 1 - "HIDDEN" // 2 - "STOP" "name": "Netflix", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345" // Available for all accessories }, { "type": "TelevisionSpeaker", "displayName": "TVSpeaker", // Not a characteristic, but a function parameter // Required Characteristics "mute": "FALSE", // Optional Characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" "volume": 10, // Range: 0-100, Step 1 "volumeControlType": "ABSOLUTE", // 0 - "NONE" // 1 - "RELATIVE" // 2 - "RELATIVE_WITH_CURRENT" // 3 - "ABSOLUTE" "volumeSelector": "INCREMENT", // 0 - "INCREMENT" // 1 - "DECREMENT" "name": "TVSpeaker", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345" // Available for all accessories } ], // Optional Characteristics "brightness": 8, // Range: 0-100, Step: 1 "closedCaptions": "DISABLED", // 0 - "DISABLED, // 1 - "ENABLED // All TLV8 Characteristics are removed by default, // unless 'allowTLV8' is set, at your own peril. // Setting most will cause Homebridge with HomeKit or Eve. // to not allow you to connect. // "DisplayOrder": 0, // Format: TLV8 "currentMediaState": "STOP", // 0 - "PLAY" // 1 - "PAUSE" // 2 - "STOP" // 3 - ??? "targetMediaState": "STOP", // 0 - "PLAY" // 1 - "PAUSE" // 2 - "STOP" // 3 - ??? "pictureMode": "STANDARD", // 0 - "OTHER" // 1 - "STANDARD" // 2 - "CALIBRATED" // 3 - "CALIBRATED_DARK" // 4 - "VIVID" // 5 - "GAME" // 6 - "COMPUTER" // 7 - "CUSTOM" "remoteKey": "SELECT", // 0 - "REWIND," // 1 - "FAST_FORWARD" // 2 - "NEXT_TRACK" // 3 - "PREVIOUS_TRACK" // 4 - "ARROW_UP" // 5 - "ARROW_DOWN" // 6 - "ARROW_LEFT" // 7 - "ARROW_RIGHT" // 8 - "SELECT" // 9 - "BACK" // 10 - "EXIT" // 11 - "PLAY_PAUSE" // 12 - "INFORMATION" // Optional Characteristics "manufacturer": "Sony", "model": "Android TV", "serialNumber": "12345", // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "active", "interval": 50, "timeout": 5000}, // {"characteristic": "activeIdentifier", "interval": 50, "timeout": 5000}, // {"characteristic": "currentMediaState", "interval": 540, "timeout": 5000} //], "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "TelevisionSpeaker", "displayName": "MyTVSpeaker", // Not a characteristic, but a function parameter // Required characteristics "mute": "FALSE", // 0 - "FALSE" // 1 - "TRUE" // Optional Characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" "volume": 5, // Range: 0-100, Step 1 "volumeControlType": "ABSOLUTE", // 0 - "NONE" // 1 - "RELATIVE" // 2 - "RELATIVE_WITH_CURRENT" // 3 - "ABSOLUTE" "volumeSelector": "INCREMENT", // 0 - "INCREMENT" // 1 - "DECREMENT" "name": "MyTVSpeaker", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "active", "interval": 50, "timeout": 5000}, // {"characteristic": "volume", "interval": 50, "timeout": 5000} //], "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "InputSource", "displayName": "MyInputSource", // Not a characteristic, but a function parameter // Required characteristics "configuredName": "MyInputSource", "inputSourceType": "HDMI", // 0 - "OTHER" // 1 - "HOME_SCREEN" // 2 - "TUNER" // 3 - "HDMI" // 4 - "COMPOSITE_VIDEO" // 5 - "S_VIDEO" // 6 - "COMPONENT_VIDEO" // 7 - "DVI" // 8 - "AIRPLAY" // 9 - "USB" // 10 - "APPLICATION" "isConfigured": "CONFIGURED", // 0 - "NOT_CONFIGURED" // 1 - "CONFIGURED" "currentVisibilityState": "SHOWN", // 0 - "SHOWN" // 1 - "HIDDEN" // Optional Characteristics "inputDeviceType": "TV", // 0 - "OTHER" // 1 - "TV" // 2 - "RECORDING" // 3 - "TUNER" // 4 - "PLAYBACK" // 5 - "AUDIO_SYSTEM" "targetVisibilityState": "SHOWN", // 0 - "SHOWN" // 1 - "HIDDEN" // 2 - "STOP" "name": "MyInputSource", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "IrrigationSystem", "displayName": "MyIrrigationSystem", // Not a characteristic, but a function parameter // Required characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" "programMode": "PROGRAM_SCHEDULED", // 0 - "NO_PROGRAM_SCHEDULED" // 1 - "PROGRAM_SCHEDULED" // 2 - "PROGRAM_SCHEDULED_MANUAL_MODE" "inUse": "IN_USE", // 0 - "NOT_IN_USE" // 1 - "IN_USE" // Optional Characteristics "remainingDuration": 60, // Range: 0-3600, Step: 1 "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "name": "MyIrrigationSystem", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "active", "interval": 50, "timeout": 5000}, // {"characteristic": "statusFault", "interval": 300, "timeout": 5000} //], "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Faucet", "displayName": "MyFaucet", // Not a characteristic, but a function parameter // Required characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" // Optional Characteristics "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "name": "MyFaucet", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific - Interval is in second, Timeout in msec // (polling per characteristic) // polling requires a state_cmd and state_cmd is // used with your own scripts. Here for example only. //"polling": [ // {"characteristic": "active", "interval": 50, "timeout": 5000}, // {"characteristic": "statusFault", "interval": 300, "timeout": 5000} //], "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "CloudRelay", // Was Relay "displayName": "MyCloudRelay", // Not a characteristic, but a function parameter // Required characteristics "relayEnabled": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "relayState": 0, // Format: UINT8 // Read-only // All TLV8 Characteristics are removed by default, // unless 'allowTLV8' is set, at your own peril. // Setting most will cause Homebridge with HomeKit or Eve. // to not allow you to connect. // "relayControlPoint": 0, // TLV8 // Optional Characteristics // - None // Cmd4 Specific "stateChangeResponseTime": 10 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "Valve", "displayName": "MyValve", // Not a characteristic, but a function parameter // Required characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" "inUse": "NOT_IN_USE", // 0 - "NOT_IN_USE" // 1 - "IN_USE" "valveType": "GENERIC_VALVE", // 0 - "GENERIC_VALVE" // 1 - "IRRIGATION" // 2 - "SHOWER_HEAD" // 3 - "WATER_FAUCET" // Optional Characteristics "setDuration": 0, // Format: Uint32 "remainingDuration": 0, // Format: Uint32 // Range: 0 - 3600, Step: 1 "isConfigured": "CONFIGURED", // 0 - "NOT_CONFIGURED" // 1 - "CONFIGURED" "serviceLabelIndex": 1, // Range: 1-255, Step: 1 "statusFault": "NO_FAULT", // 0 - "NO_FAULT" // 1 - "GENERAL_FAULT" "name": "MyValve", "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "HeaterCooler", "displayName": "MyHeaterCooler", // Not a characteristic, but a function parameter // Required characteristics "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" "currentHeaterCoolerState": "HEATING", // 0 - "INACTIVE" // 1 = "IDLE" // 2 - "HEATING" // 3 = "COOLING" "targetHeaterCoolerState": "AUTO", // 0 - "AUTO" // 1 - "HEAT" // 2 - "COOL" "currentTemperature": 37.0, // Range: 0-100, Step: 0.1 // Format: float // "Units": CELCIUS // Optional Characteristics "lockPhysicalControls": "CONTROL_LOCK_DISABLED", // 0 - "CONTROL_LOCK_DISABLED" // 1 - "CONTROL_LOCK_ENABLED" "name": "MyHeaterCooler", "swingMode": "SWING_ENABLED", // 0 - "SWING_DISABLED", // 1 - "SWING_ENABLED" "coolingThresholdTemperature": 22.2, // Range 10-35, Step: 0.1 // Units: CELCIUS "heatingThresholdTemperature": 25.2, // Range 0-25, Step: 0.1 // Format: float // Units: CELCIUS "temperatureDisplayUnits": "CELSIUS", // 0 - "CELSIUS" // 1 - "FAHRENHEIT" "rotationSpeed": 100, // Range: 0-100, Step: 1 "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "HumidifierDehumidifier", "displayName": "MyHumidifierDehumidifier",// Not a characteristic, but a function parameter // Required characteristics "currentRelativeHumidity": 60, // Range: 0-100, Step: 1 // Format: float // Units: Percentage "currentHumidifierDehumidifierState": "HUMIDIFYING", // 0 - "INACTIVE" // 1 - "IDLE" // 2 - "HUMIDIFYING" // 3 - "DEHUMIDIFYING" "targetHumidifierDehumidifierState": "HUMIDIFIER", // 0 - "HUMIDIFIER_OR_DEHUMIDIFIER" // 1 - "HUMIDIFIER" // 2 - "DEHUMIDIFIER" "active": "ACTIVE", // 0 - "INACTIVE" // 1 - "ACTIVE" // Optional Characteristics "lockPhysicalControls": "CONTROL_LOCK_DISABLED", // 0 - "CONTROL_LOCK_DISABLED" // 1 - "CONTROL_LOCK_ENABLED" "name": "MyHumidifierDehumidifier", "swingMode": "SWING_DISABLED", // 0 - "SWING_DISABLED" // 1 - "SWING_ENABLED" "waterLevel": 0, // Range: 0 - 100 // Format: float "relativeHumidityDehumidifierThreshold": 0, // Range: 0 - 100, Step 1 // Format: float // Units: Percentage "relativeHumidityHumidifierThreshold": 0, // Range: 0 - 100, Step 1 // Format: float // Units: Percentage "rotationSpeed": 0, // Range: 0 - 100, Step 1 "manufacturer": "Somebody", // Available for all accessories "model": "Anything", // Available for all accessories "serialNumber": "12345", // Available for all accessories // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "AccessControl", "displayName": "MyAccessControl", // Not a characteristic, but a function parameter // Required characteristics "accessControlLevel": 0, // 0-2 // Cmd4 Specific "stateChangeResponseTime": 3 // Default (Seconds) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "CameraOperatingMode", "displayName": "MyCameraOperatingMode", // Not a characteristic, but a function parameter // Required characteristics "eventSnapshotsActive": "DISABLE", // 0 - "DISABLE" // 1 - "ENABLE" "homeKitCameraActive": "OFF", // 0 - "OFF" // 1 - "ON" // Optional Characteristics "manuallyDisabled": "DISABLED", // 0 - "ENABLED" // 1 - "DISABLED" "nightVision": "FALSE", // 0 - "FALSE" // 1 - "TRUE" "thirdPartyCameraActive": "OFF", // 0 - "OFF" // 1 - "ON" "periodicSnapshotsActive": "DISABLE" // 0 - "DISABLE" // 1 - "ENABLE" // Cmd4 Specific //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "SmartSpeaker", "displayName": "MySmartSpeaker", // Not a characteristic, but a function parameter // Required characteristics "currentMediaState": "STOP", // 0 - "PLAY" // 1 - "PAUSE" // 2 - "STOP" // 3 - ??? "targetMediaState": "STOP", // 0 - "PLAY" // 1 - "PAUSE" // 2 - "STOP" // 3 - ??? // Optional Characteristics "name": "MySmartSpeaker", "configuredName": "MySmartSpeaker", "volume": 5, "mute": "FALSE" // 0 - "FALSE" (Off) // 1 - "TRUE" (On) //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "TargetControl", "displayName": "MyTargetControl", // Not a characteristic, but a function parameter // Required characteristics "activeIdentifier": 1183, "active": "ACTIVE" // 0 - "INACTIVE" // 1 - "ACTIVE" // All TLV8 Characteristics are removed by default, // unless 'allowTLV8' is set, at your own peril. // Setting most will cause Homebridge with HomeKit or Eve. // to not allow you to connect. //"buttonEvent": 0, // TLV8 //"state_cmd": "node .homebridge/< yourScriptHere >" }, { "type": "WiFiSatellite", "displayName": "MyWiFiSatellite", // Not a characteristic, but a function parameter // Required characteristics "wifiSatelliteStatus": "NOT_CONNECTED" // 0 - UNKNOWN // 1 - CONNECTED // 2 - NOT_CONNECTED // Cmd4 Specific //"state_cmd": "node .homebridge/< yourScriptHere >" } ] } ] } ================================================ FILE: Extras/jsmin.c ================================================ /* jsmin.c Copyright (c) 2017 John Talbot (www.BeNiceGames.com) - Inspired by jsmin from Douglas Crockford (www.crockford.com) - As compared to his, This jsmin leaves the output uncompacted so that it is legible and ONLY removes C & C++ style comments. - This code was rewritten from scratch, only the Permissions are the same, but Kudos to Douglas for coming up with a solution for allowing comments in json files. I just needed something that I could debug the output of more easily. - Execution is the same: jsmin < config.min.json > config.json Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The Software shall be used for Good, not Evil. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include int theCurrentChar; int theNextChar; int insideSingleQuote = 0; int insideDoubleQuote = 0; unsigned long theCharacterPositionInLine = 0; unsigned long lineCounter =1; // Return the next character from stdin. increasing line number count accordingly. int getCharFromStdin() { int c = getc(stdin); if (c == '\r') c = '\n'; if (c == '\n') { lineCounter ++; theCharacterPositionInLine = 0; } else { theCharacterPositionInLine ++; } return c; } // jsmin -- Copy stdin to stdout, deleting 'C' or 'C++' style comments. // -- Everything else is left the same, except: // - any \r characters are changed to \n. // - any empty blank lines are removed. // - any trailing blanks are removed. // Errors: The only error possible is for unterminated 'C' style comments. void outputChar(int c) { static unsigned long consecutiveSpaceCounter = 0; static int printableCharFoundOnLine = 0; // Do not print anything if no characters were on the line. if (c == '\n' && printableCharFoundOnLine == 0) { consecutiveSpaceCounter = 0; return; } // Count the spaces before printing them so that empty lines // are removed. (This looks better for blank lines with comments removed). if (c == ' ') { consecutiveSpaceCounter ++; } else { // This will print any spaces counted before a real character // and not when EOL is found. if (c != '\n') { // Since consecutiveSpaceCounter is unsigned, there is no // possability of being stuck in a loop forever. while (consecutiveSpaceCounter != 0) { putc(' ', stdout); consecutiveSpaceCounter --; } printableCharFoundOnLine = 1; } else { // A new line means the next line needs to reset char found flag. printableCharFoundOnLine = 0 ; } putc(c, stdout); // Since a char was printed, reset the space counter. consecutiveSpaceCounter = 0; } } static void jsmin() { // Prime the pump. theCurrentChar = getCharFromStdin(); while (theCurrentChar != EOF) { if (theCurrentChar == '\'') { insideSingleQuote = 1; outputChar(theCurrentChar); theCurrentChar = getCharFromStdin(); // Continue until out of single quotes // or forced out of single quotes by a newline while (insideSingleQuote == 1 && theCurrentChar !=EOF) { theNextChar = getCharFromStdin(); // Skip passed escaped single quotes if (theCurrentChar == '\\' && theNextChar == '\'' ) { outputChar(theCurrentChar); theCurrentChar = theNextChar; continue; } // Found end of single quotes if (theCurrentChar == '\'') { insideSingleQuote = 0; } // Newline and inside single quotes breaks // out automatically if (theCurrentChar == '\n') { insideSingleQuote = 0; } outputChar(theCurrentChar); theCurrentChar = theNextChar; } } if (theCurrentChar == '\"') { insideDoubleQuote = 1; outputChar(theCurrentChar); theCurrentChar = getCharFromStdin(); // Continue until out of single quotes // or forced out of single quotes by a newline while (insideDoubleQuote == 1 && theCurrentChar !=EOF) { theNextChar = getCharFromStdin(); // Skip passed escaped double quotes if (theCurrentChar == '\\' && theNextChar == '\"' ) { outputChar(theCurrentChar); outputChar(theNextChar); theCurrentChar = getCharFromStdin(); theNextChar = getCharFromStdin(); } // Found end of double quotes if (theCurrentChar == '\"') { insideDoubleQuote = 0; } // Newline and inside double quotes breaks // out automatically if (theCurrentChar == '\n') { insideDoubleQuote = 0; } outputChar(theCurrentChar); theCurrentChar = theNextChar; } } // Look for the start of a 'C' or 'C++' style comment. if (theCurrentChar == '/' ) { theNextChar = getCharFromStdin(); // Found a 'C++' style comment. if (theNextChar == '/' ) { // Continue until \n while (theCurrentChar != '\n' ) { theCurrentChar = getCharFromStdin(); if (theCurrentChar == EOF) break; } outputChar(theCurrentChar); // The EOL char theCurrentChar = getCharFromStdin(); continue; // Main loop. } if (theNextChar == '*') // Found a 'C' style comment. { unsigned long openCommentLineNumber = lineCounter; // Move past the start of the comment theCurrentChar = getCharFromStdin(); theNextChar = getCharFromStdin(); // Continue until '*/' while (theCurrentChar != '*' || theNextChar != '/' ) { if (theCurrentChar == EOF) { fprintf(stderr, "Missing comment terminator for line %lu\n\n", openCommentLineNumber); exit(-1); } theCurrentChar = theNextChar; theNextChar = getCharFromStdin(); } theCurrentChar = getCharFromStdin(); continue; // The main loop. } outputChar(theCurrentChar); // The next char has already been read, use it. theCurrentChar = theNextChar; continue; // The main loop. } outputChar(theCurrentChar); // Read the char to be processed next. theCurrentChar = getCharFromStdin(); } } // main -- Output any command line arguments to stderr and then minify the input. extern int main(int argc, char* argv[]) { int i; for (i = 1; i < argc; i += 1) { fprintf(stderr, "%s\n", argv[i]); } jsmin(); return 0; } ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ [![npm](https://badgen.net/npm/v/homebridge-cmd4/latest?icon=npm&label)](https://www.npmjs.com/package/homebridge-cmd4) [![npm](https://badgen.net/npm/dt/homebridge-cmd4?label=downloads)](https://www.npmjs.com/package/homebridge-cmd4) # Homebridges-cmd4 - CMD4 Plugin for Homebridge - Supports ~All Accessory Types and now all Characteristics, Custom Characteristics and Priority Queued Polling too!!

## Cmd4 No longer supported: As of September 2025, I will no longer be supporting Cmd4.
Cmd4 was cool in that it allowed me to discover how the Apple Home App can control all the different devices, but there are a number of reasons why I've decided to no longer support Cmd4.
  • Apple took all the fun away from a lot of the devices. You can no longer do Apple TV for instance. Trying to keep up with Apple's API changes is a fast moving target. HomeBridge tries to minimize this but I have no interest in keeping up with them.
  • When homebridge went to TypeScript I was not a fan. Personally the code bloat that it offers is not worth the effort as I believe good Unit testing and for javascript, JSLint is always a better method. I am also not a fan of Swift for the same reasons. Just try to do a bit shift in Swift.
  • Homebridge config-UI-X was always nasty for Cmd4. The latest beta of Config-UI-X has gone on for almost a year and it is probably because they too went to TypeScript.
  • My health is not what it used to be. My little time left on this planet has no interest in supporting Cmd4.
So I did update Cmd4 for the latest Homebridge release which is still in Beta. It is not likely that I will pay any more attention to Cmd4.

##     **Cmd4** is a plugin for [Homebridge] that lets you see what Homebridge and Homekit are all about, without actually owning a single accessory! For the more advanced user, Cmd4 brings CLI support for all device types (Accessories) and characteristics. ## Homebridge UI User. Do not click on any inline links. Homebridge UI does not supprt them. Bug#1098

## Table of Contents * [**About the Cmd4 Plugin**](#about-the-cmd4-plugin) * [**How the Cmd4 Plugin Works**](#how-the-cmd4-plugin-works) * [**Features**](#features) * [**ChangeLog**](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/CHANGELOG.md) * [**Screenshots**](#screenshots) * [**Installation Details**](#installation-details) * [**Demo Mode**](#demo-mode) * [**Basic Troubleshooting**](#basic-troubleshooting) * [***Error: Command failed***](#error-command-failed) * [***Debug Steps***](#debug-steps) * [**Advanced Troubleshooting For Developers**](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/docs/AdvancedTroubleShooting.md) * [**Developers Guide**](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/docs/Developers.md) * [**Rationale**](#rationale) * [**Todo**](#todo) * [**Contributing**](#contributing) * [**Inspiration and special thanks**](#inspiration-and-special-thanks) * [**License**](#license)

## About the Cmd4 Plugin     This plugin allows you to see what Homebridge is all about, without having any home control accessories. If you can run Homebridge and install this plugin, all the possible accessories are now available to you within HomeKit or Eve. (Except Camera Streaming).     Similar to other command plugins, you can use Cmd4 to execute command line interface (CLI) scripts, but with Cmd4 the devices don't just appear in HomeKit as a Switch or a Light, but now they can appear as a Garage Door opener, Air Purifier ... and you can control all of their characteristics too.

## How the Cmd4 Plugin Works     Cmd4 comes with a fully populated and documented config.json file that is set to using just the cached entry's in the config file itself. This makes HomeKit behave as if you actually have all these Accessories! If you choose to add in your own scripts, the Cmd4 Github pages at [ http://ztalbot2000.github.io/homebridge-cmd4](https://ztalbot2000.github.io/homebridge-cmd4) have example script templates and further development instructions.

## Features     Cmd4 supports, Lights, Garage Door Openers, Outlets, Switches, Lock Maintenance Systems, Lock Management Systems, Humidity Sensors, Doors, Light Sensors, Temperature Sensors, Contact Sensors, Motion Sensors, Thermostats, Security Systems, Battery Services, Filter Maintenance Systems, Air Purifiers, Television, Television Speaker, Input Sources, Irrigation Systems, ... everything but Camera Streaming since it is not pliable to a command line Interface.     Cmd4 Polling is a way to in the background update the state of devices via the 'polling' dirctive.
  • "polling": "on" - Implements polling of the default characteristics of the defined "timeout" and "stateChangeResponseTime".
  • "polling": [{ "characteristic": "", [ "timeout": , "stateChangeResponseTime": ] }] - implements per characteristic polling.
    Cmd4 supports Fakegato History and retaining previous state over restarts.     Since version 2.0 Cmd4 has supported linked Accessories and the latest 3.0 Version supports both Platform plugin and Accessory plugin configurations as documented on [developers.homebridge.io](https://developers.homebridge.io).     Cmd4 Version 3.5 adds in support for Custom Characteristics.     Cmd4 Version 3.6 allows you to configure Priority Queued Polling so that the accessory only receives one request at a time; with priority to all IOS requests first and polling second.

## Screenshots



## Installation Details ### Step 1. Install Homebridge See [homebridge](https://github.com/homebridge/homebridge) for complete details.
### Step 2. Install Cmd4 Plugin ```bash *SHELL*> sudo npm install -g --unsafe-perm homebridge-cmd4 ```
### Step 3. Install/Update your config.json file     Use the provided config.json file or add it to your own. ```bash *SHELL*> cp /usr/local/lib/node_modules/homebridge-cmd4/Extras/config.json $HOME/.homebridge/ ``` ### Step 4. Restart homebridge See [homebridge](https://github.com/homebridge/homebridge) for complete details.
### Step 5. Try Homekit     If you are not already familiar with Homekit, you may wish to look at the documentation for Homebridge and how to configure it with Homekit. The gist of it is that you enter the manual code defined in the config.json file. I chose 5555555 for simplicity. ### That's it! Enjoy all your new Virtual Accessories!. ✅

## Demo Mode     The config.json provided has no polling defined. Demo mode is achieved when no polling of any kind for that accessory is defined. In this way all values are retrieved and set from cache.

## Basic Troubleshooting ### Error: Command failed     This error is indicative to many situations. The most common is that the command took to long to run. ``` Error: Command failed: /homebridge/Server.sh Get 'Server' 'On' at ChildProcess.exithandler (child_process.js:297:12) at ChildProcess.emit (events.js:193:13) at maybeClose (internal/child_process.js:1001:16) at Process.ChildProcess._handle.onexit (internal/child_process.js:266:5) killed: true code: null signal: SIGTERM, cmd: "/homebridge/Server.sh Get Server On" ``` Check that the command exists, but also that the timeout value in your config.json for that accessory is not too low. ### Debug Steps New in Cmd4 v4.0.0 is how to enable Debug mode. The logs are 100% the same, except that now that Cmd4 has its own logging system ( Copied from Homebridge for compatability ); Enabling Debug logs will not enable Debug logs in other plugins.
There are two ways to enable Cmd4 Debug logs. #### Method 1. Modifying the Cmd4 Platform section     The Cmd4 Platform section can contain the enable Debug directive. ```json { "platform": "Cmd4", "name": "Cmd4", "debug": true } ``` #### Method 2. Add DEBUG environental variable ```bash *SHELL*> DEBUG=Cmd4 ``` Note: For Homebridge-config-ui-x, you only need to write Cmd4 in the Environmental variable section.

## Rationale     After playing with homebridge-cmd and then homebridge-cmdswitch2, I really wanted to control as much as I could. I did not have all the devices, but I did have a light and a Sony Android TV. I wanted to control these and see what else I could do.
    If you were wondering what happened to version 3, well I learned a lot, hence the working cmd4.

## Todo * [ ] Become Homebridge Certified. ( The schema file for Homebridg-ui would be very difficult )

## Contributing     Best ways to contribute * Star it on GitHub - if you use it and like it please at least star it :) * [Promote](#promotion) * Open [issues/tickets](https://github.com/ztalbot2000/homebridge-cmd4/issues) * Submit fixes and/or improvements with [Pull Requests](https://github.com/ztalbot2000/homebridge-cmd4/pulls) ### Promotion     Like the project? Please support to ensure continued development going forward: * Star this repo on [GitHub][homebridge-cmd4-repo] * Follow the repo on [GitHub][homebridge-cmd4-repo] * Follow me * [ztalbot2000](https://github.com/ztalbot2000)

## Inspiration and special thanks Based on the best of: * [homebridge-cmd]https://github.com/senyoltw/homebridge-cmd.git * [homebridge-cmdswitch2]https://github.com/luisiam/homebridge-cmdswitch2.git * [homebridge-real-fake-garage-doors]https://github.com/plasticrake/homebridge-real-fake-garage-doors.git * [homebridge-homeseer]https://github.com/jrhubott/homebridge-homeseer.git * Thanks to [TimofeyK](https://github.com/TimofeyK) for all his help with fakegato and for suggesting it. * Thanks to [Mitch Williams](https://github.com/mitch7391) for his endless encouragement. * Thanks especially to those who continue to work on Homebridge and those who make it better. * And most importantly my Wife.

## License See [LICENSE](LICENSE) [homebridge]:https://github.com/homebridge/homebridge [developers.homebridge.io]:https://developers.homebridge.io/#/ [homekit_screenshot]:https://github.com/ztalbot2000/homebridge-cmd4/raw/master/screenshots/Homekit_screenshot.png [eve_screenshot]:https://github.com/ztalbot2000/homebridge-cmd4/raw/master/screenshots/Eve_screenshot.png [ztalbot2000]:https://github.com/ztalbot2000 ================================================ FILE: RUNNING_CHANGELOG.md ================================================ # Homebridges-cmd4 - CMD4 Plugin for Homebridge - Supports ~All Accessory Types and now all Characteristics too!! ## Homebridge UI User. Do not click on any inline links. Homebridge UI does not supprt them. Bug#1098 ## ChangeLog * **Whats new in 8.0.4** * **Whats new in 8.0.3** * **Whats new in 8.0.2** * **Whats new in 8.0.1** * **Whats new in 8.0.0** * **Whats new in 7.1.0** * **Whats new in 7.0.3** * **Whats new in 7.0.2** ## Whats new in 7.0.0 #### 7.0.0 (2023-06-11) * **Whats new in 7.0.0** * **Whats new in 6.3.0** * **Whats new in 6.2.0** * **Whats new in 6.1.2** * **Whats new in 6.1.1** * **Whats new in 6.1.0** * **Whats new in 6.0.4** * **Whats new in 6.0.3** * **Whats new in 6.0.2** * **Whats new in 6.0.1** * **Whats new in 6.0.0** * **Whats new in 6.0.0.beta1** * **Whats new in 5.1.1** * **Whats new in 5.1.0** * **Whats new in 5.0.2** * **Whats new in 5.0.1** * **Whats new in 5.0.0** * **Whats new in 4.2.3** * **Whats new in 4.2.2** * **Whats new in 4.2.1** * **Whats new in 4.2.0** * **Whats new in 4.0.1** * **Whats new in 4.0.0** * **Whats new in 3.10.1** * **Whats new in 3.10.0** * **Whats new in 3.9.0** * **Whats new in 3.8.1** * **Whats new in 3.8.0** * **Whats new in 3.7.5** * **Whats new in 3.7.4** * **Whats new in 3.7.3** * **Whats new in 3.7.2** * **Whats new in 3.7.1** * **Whats new in 3.7.0** * **Whats new in 3.6.1** * **Whats new in 3.6.0** * **Whats new in 3.5.1** * **Whats new in 3.5.0** * **Whats new in 3.4.4** * **Whats new in 3.4.3** * **Whats new in 3.4.2** * **Whats new in 3.4.1** * **Whats new in 3.4.0** * **Whats new in 3.3.2** * **Whats new in 3.3.1** * **Whats new in 3.3.0** * **Whats new in 3.2.5-beta6** * **Whats new in 3.2.5-beta5** * **Whats new in 3.2.5-beta3** * **Whats new in 3.2.5-beta2** * **Whats new in 3.2.5-beta1** * **Whats new in 3.2.3** * **Whats new in 3.2.2** * **Whats new in 3.2.1** * **Whats new in 3.2.0** * **Whats new in 3.1.3** * **Whats new in 3.1.2** * **Whats new in 3.1.1** * **Whats new in 3.1.0** * **Whats new in 3.0.15** * **Whats new in 3.0.14** * **Whats new in 3.0.12** * **Whats new in 3.0.11** * **Whats new in 3.0.9** * **Whats new in 3.0.8** * **Whats new in 3.0.7** * **Whats new in 3.0.5** * **Whats new in 3.0.4** * **Whats new in 3.0.0** * **Whats new in 2.4.2** * **Whats new in 2.4.1** * **Whats new in 2.4.0** * **Whats new in 2.3.2** * **Whats new in 2.3.1** * **Whats new in 2.3.0** * **Whats new in 2.2.5** * **Whats new in 2.2.2** * **Whats new in 2.2.1** * **Whats new in 2.2** * **Whats new in 2.1** * **Whats new in 2.0** ## Whats new in 8.0.4 #### 8.0.4 (2026-02-03) ##### Bug Fixes * Support Node v24 ([feb476b5](https://github.com/ztalbot2000/homebridge-cmd4/commit/feb476b5bbea98099f7ebaf4a8dcea03572dac82)) ## Whats new in 8.0.3 #### 8.0.3 (2024-12-11) ##### Bug Fixes * Update node requirements to satisfy the current Node.js version of v22.12.0 ([22761c89](https://github.com/ztalbot2000/homebridge-cmd4/commit/22761c8954dfdb6086023585978836b36f531f65)) ## Whats new in 8.0.2 #### 8.0.2 (2024-10-20) ##### Bug Fixes * github-version-checker is an unused dependency and is also obsolete, so remove it ([ad220e82](https://github.com/ztalbot2000/homebridge-cmd4/commit/ad220e82b66accf630021dc8c4c136948b720acd)) * Also for 146, put back reachable as per Wongs fix ([0e131097](https://github.com/ztalbot2000/homebridge-cmd4/commit/0e131097087899cad530c132b699639e384b3ce0)) ## Whats new in 8.0.1 #### 8.0.1 (2024-08-16) ##### Bug Fixes * Bug [#147](https://github.com/ztalbot2000/homebridge-cmd4/pull/147) Accesories removed from home app after upgrade to 8.0 ([de25e94f](https://github.com/ztalbot2000/homebridge-cmd4/commit/de25e94fac8cf331142ea4523218db0f8cf447b0)) ## Whats new in 8.0.0 ### 8.0.0 (2024-06-28) ##### Documentation Changes * Document changes for homebridge v2 where TargetAirQuality and TargetSlatState are deprecated ([80ef143](https://github.com/ztalbot2000/homebridge-cmd4/commit/80ef143fa68ff7abedd6bcf4b715d2dab3ae8451)) ##### Bug Fixes * fix for homebridge v2 where TargetSlateStte and TargetAirQuality are deprecated ([6acfbb2](https://github.com/ztalbot2000/homebridge-cmd4/commit/6acfbb26bf56a355d76f3f96dafabdfefd26bbb6)) * Fix for homebridge v2 Formats&Perms&Units only available directly from HAP ([2a9d6e9](https://github.com/ztalbot2000/homebridge-cmd4/commit/2a9d6e976647f1fa7e1a3ee34e5080a3c28eae73)) * Fix lint error ([286dc68](https://github.com/ztalbot2000/homebridge-cmd4/commit/286dc68441c776a93f0f0d913db326fa9ccafeec)) * Fix add beta tag ([9b36408](https://github.com/ztalbot2000/homebridge-cmd4/commit/9b364087ec489c5dfda503ceaf1429b626a929c0)) * Fix for homebridge v2 where some device types are deprecated ([e483326](https://github.com/ztalbot2000/homebridge-cmd4/commit/e483326d93d447f81be5978f337ce80834798145)) * stop committing npm package ([630f4fb](https://github.com/ztalbot2000/homebridge-cmd4/commit/630f4fb6f6cbd876b3fe21ee4689eb40a6adeacc)) * Fix: Update hap perms for homebridge v2 & deprecate devices for name changes ([5eb9ede](https://github.com/ztalbot2000/homebridge-cmd4/commit/5eb9ede93de6b80920defeacf704e7c560ee9bda)) * Fix: For homebridge V2 which removed Perms.READ and Perms.WRITE for PAIRED_READ & PAIRED_WRITE ([5e888b0](https://github.com/ztalbot2000/homebridge-cmd4/commit/5e888b09cee178ec722499e5429f2dba4fb5c8a9)) * Fix: homebridge V2 requires updated package fakegato-history ([61011e5](https://github.com/ztalbot2000/homebridge-cmd4/commit/61011e58378aff7eab351cc97ce05a167f759ca2)) * Fix: Set read for h omebridge v2 & Remove deprecated Characteristic.getValue() tests ([b1a8702](https://github.com/ztalbot2000/homebridge-cmd4/commit/b1a870288d73bdcd7abb26a439b9d8e722ad53ea)) ##### New Features * Support only Platform Accessories as per Homebridge recommendation ([0ee6efc](https://github.com/ztalbot2000/homebridge-cmd4/commit/0ee6efcc09b7c87f346299fb9ccab5940fba7ee0)) ## Whats new in 7.1.0 ### 7.1.0 (2024-03-04) ##### Documentation Changes * Update for newest characteristics ([fccb9ebd](https://github.com/ztalbot2000/homebridge-cmd4/commit/fccb9ebdc18a2cd876b1512e0da2d5d28e8019b0)) ##### New Features * Add in latest new characteristics and devices ([545112f0](https://github.com/ztalbot2000/homebridge-cmd4/commit/545112f0fa2122abf51d3217707012879db723b7)) ## Whats new in 7.0.3 #### 7.0.3 (2024-01-27) ##### Bug Fixes * props is already an object. Also test setProps is called for more than one characteristic. Affects only those who change a characteristics property range ([276d0c0d](https://github.com/ztalbot2000/homebridge-cmd4/commit/276d0c0d58b435c59fb3c1fdfd322f55f6b24434)) ## Whats new in 7.0.2 ### 7.0.2 (2023-12-24) ##### Change affectng AdvAir by their request * For AdvAir.sh put accessory name in double quotes so it can contain a single quote ([023e91c0](https://github.com/ztalbot2000/homebridge-cmd4/commit/023e91c0bf1794a64bf0e18dec7d31b178ecb6d1)) ## ChangeLog * **Whats new in 7.0.1** ##### Chores * Correct version to 7.0.1 ([62504e68](https://github.com/ztalbot2000/homebridge-cmd4/commit/62504e689a9483a9512a414d994514b04cbadcd5)) ##### Bug Fixes * bug[#139](https://github.com/ztalbot2000/homebridge-cmd4/pull/139), add in link checkers to documentation ([f4a1ae79](https://github.com/ztalbot2000/homebridge-cmd4/commit/f4a1ae79fd8da2e0dc80181ab98dbc4817da6e78)) ## Whats new in 7.0.0 #### 7.0.0 (2023-03-05) * The reason for the major version numbering update was to for deprecation warnings to throw an error. These warnings were there for many major versions and are finally removed. "queueInterval" - Never put into production and will now throw an error. "queueMsg" - Never put into production and will now throw an error. "queueStatMsgInterval" - Never put into production and will now throw an error. "cmd4_Mode" - Obsolete for many versions and will now throw an error. "storage" - Obsolete outside of fakegato definition. "storagePath" - Obsolete outside of fakegato definition. "folder" - Obsolete outside of fakegato definition. "keyPath" - Obsolete outside of fakegato definition. "queueTypes" - Obsolete inside accessory definition. ##### New Features * add retries. Also formatting issues ([b8ddf9ce](https://github.com/ztalbot2000/homebridge-cmd4/commit/b8ddf9ceda8ace3a6e820bfad0c4a03c656cf419)) ##### Documentation Changes Beta2 * Update documentation for v7.0.0 ([12b6482a](https://github.com/ztalbot2000/homebridge-cmd4/commit/12b6482ac8e6d431e011f6f92c2d6f03a2ebe2ed)) ##### Bug Fixes Beta2 * remove unwanted debug message and test so that it cannot reoccur ([97b6b318](https://github.com/ztalbot2000/homebridge-cmd4/commit/97b6b31861ab34d04b798ec409b1f2e8c56244c2)) * Homebridge does not like underscores of accessory names. Remove from default config.json ([15f2170b](https://github.com/ztalbot2000/homebridge-cmd4/commit/15f2170be492b6dfb56e4eed5d76da0cd4d74419)) Beta1 * retry logging starts from zero, but for user count starts from 1 ([38cdf417](https://github.com/ztalbot2000/homebridge-cmd4/commit/38cdf417feebeef2b75dcfec247e6bdfd4739604)) * By nature retryCount should start from zero ([b8e82ffe](https://github.com/ztalbot2000/homebridge-cmd4/commit/b8e82ffe262c092a301636993544be828a7a34a4)) * log message in wrong quotes ([6fc555b0](https://github.com/ztalbot2000/homebridge-cmd4/commit/6fc555b0d7bdc20bfc3a38310e3d4f66b968d55d)) * retries for WoRm should be zero. Can be overriden ([11351633](https://github.com/ztalbot2000/homebridge-cmd4/commit/1135163319c8d16e3886dbf0a0443ba5ed6c4b99)) * Accessory needs to go unresponsive via updateValue with error object ([cdfee1b2](https://github.com/ztalbot2000/homebridge-cmd4/commit/cdfee1b22ea21dc56edc320768ec6ebea1156cb8)) ##### Tests * put back skipped tests ([0b0781fa](https://github.com/ztalbot2000/homebridge-cmd4/commit/0b0781faec7f94e43d67068e15e9505cc7c96414)) * Remove excess debug messages for commit with verify to work ([ea108470](https://github.com/ztalbot2000/homebridge-cmd4/commit/ea108470631f4abe3377f6d582c962dab141be0f)) * Don't know why HomeKit does not cause Cmd4 to generate a log that it is trying to reconnect after unavailable ([d45d7c09](https://github.com/ztalbot2000/homebridge-cmd4/commit/d45d7c095d0989c05b88c161a418402711e26677)) * Add unit testing to prove fix of bug[#130](https://github.com/ztalbot2000/homebridge-cmd4/pull/130) ([e9a902fc](https://github.com/ztalbot2000/homebridge-cmd4/commit/e9a902fcb01fb2ee7c7cb9f2b60e036d0fd5c0d6)) ## Whats new in 6.3.0 ### 6.3.0 (2022-04-12) ##### Bug Fixes * Bug [#130](https://github.com/ztalbot2000/homebridge-cmd4/pull/130), linked accessories not accesible after restart ([aeab5a40](https://github.com/ztalbot2000/homebridge-cmd4/commit/aeab5a40340631910ae6ac19f9af8453f46faa55)) ## Whats new in 6.2.0 ## 6.2.0 (2022-02-18) ##### New Features * Add new characteristics and device types ([c3560130](https://github.com/ztalbot2000/homebridge-cmd4/commit/c3560130e7092b698d0d750924c548a99c4e20f4)) Characteristics --------------- AssetUpdateReadiness SupportedAssetTypes ConfigurationState NFCAccessControlPoint NFCAccessSupportedConfiguration SiriEndpointSessionStatus ThreadControlPoint ThreadNodeCapabilities ThreadStatus ThreadOpenThreadVersion Device Types ------------ AccessoryMetrics AssetUpdate Assistant AudioStreamManagement Battery CameraRecordingManagement CloudRelay DataStreamTransportManagement NFCAccess SiriEndpoint ThreadTransport Note: While these are now available to use. Some may not be suitable for Cmd4 depending on their complexity. ## Whats new in 6.1.2 #### 6.1.2 (2022-02-12) ##### Bug Fixes * bug [#127](https://github.com/ztalbot2000/homebridge-cmd4/pull/127") Resolve crash when no internet connection ## Whats new in 6.1.1 #### 6.1.1 (2022-01-31) ##### Bug Fixes * bug [#125](https://github.com/ztalbot2000/homebridge-cmd4/pull/125) Manufacturer, serialNumber and Model not displaying in Home ([715a2127](https://github.com/ztalbot2000/homebridge-cmd4/commit/715a21275229ffb395b98a2e7c4f9f3d3fd785dd)) ## Whats new in 6.1.0 ### 6.1.0 (2022-01-30) ##### New Features * See if WoRm2 Queue can resolve large scale AirCon accessories ([61b164ae](https://github.com/ztalbot2000/homebridge-cmd4/commit/61b164ae11ffb4731da0ac60bba51d7fbeefff94)) ##### Bug Fixes * bug [#124](https://github.com/ztalbot2000/homebridge-cmd4/pull/124), Eve graph not being displayed ([929da3e3](https://github.com/ztalbot2000/homebridge-cmd4/commit/929da3e39548df645d316365ab1273d6b5ed250d)) * PROGRAM_SCHEDULED_MANUAL_MODE_ constant remove trailing underscore ([52e1fd3f](https://github.com/ztalbot2000/homebridge-cmd4/commit/52e1fd3f100e18588a171c96b8ef9f511180c1bd)) ## Whats new in 6.0.4 #### 6.0.4 (2021-12-31) ##### Bug Fixes * Constants were not getting passed down to linked types ([b7252bf9](https://github.com/ztalbot2000/homebridge-cmd4/commit/b7252bf934e26535047008f343f4d6b627dd6ddb)) ## Whats new in 6.0.3 ### 6.0.3 (2021-11-11) ##### Bug Fix * Schema Prep for Homebridge-UI integration ([2c5df2c1](https://github.com/ztalbot2000/homebridge-cmd4/commit/2c5df2c1279ebe88fdaedbaa22ae0124e2841b6d)) ## Whats new in 6.0.2 ### 6.0.2 (2021-11-10) ##### Bug Fix * Schema Prep - For Homebridge-ui schema must have defined keys ([3053780b](https://github.com/ztalbot2000/homebridge-cmd4/commit/3053780b40eb3d4505dc407411257109977688a1)) ## Whats new in 6.0.1 #### 6.0.1 (2021-11-07) ##### Bug Fixes * For bug[#116](https://github.com/ztalbot2000/homebridge-cmd4/pull/116) remove stateCmd validation and for AirCon add global const parsing ([e7bc4963](https://github.com/ztalbot2000/homebridge-cmd4/commit/e7bc49630c47d16d296ff9751df7c7666cf2ecd3)) ## Whats new in 6.0.0 ## 6.0.0 (2021-10-01) * Schema Prep - Found in Beta Check case of type ([9a394de0](https://github.com/ztalbot2000/homebridge-cmd4/commit/9a394de020b2ccf67a9c30194163807ea3a226a9)) ## Whats new in 6.0.0-beta1 ## 6.0.0-beta1 (2021-09-27) ##### New Features * Schema Prep - More documentation changes ([c6d68f1f](https://github.com/ztalbot2000/homebridge-cmd4/commit/c6d68f1f8a7c24e786a54dbf1660dba98a5d1b6a)) * Schema Prep - Documentation changes for feat ([5be7393d](https://github.com/ztalbot2000/homebridge-cmd4/commit/5be7393d4b722c602862a382b1bd710556ff196a)) * Schema Prep. More fakegato testing, Make cmd4Dbg not be file global scope ([50355797](https://github.com/ztalbot2000/homebridge-cmd4/commit/503557973758fd4103ab13f34680a4f08eaed3c2)) * Schema Prep - for fakegato and others, correct key/value pair the first time it is found ([335ca7e5](https://github.com/ztalbot2000/homebridge-cmd4/commit/335ca7e50958a089c21bfac61ee1982756b7965b)) * Schema Prep - Make sure fakegato characteristics are correct ([a8274c7a](https://github.com/ztalbot2000/homebridge-cmd4/commit/a8274c7adb9ffdc2c34d048057f8334d6da9ef8a)) * Schema Prep - Should save by index, like others ([20872e2b](https://github.com/ztalbot2000/homebridge-cmd4/commit/20872e2b0f8a5c52131bb9247ba6e6ae11b20fb4)) * Schema-Prep This works with all lower case, still sends Get/Set upper case ([d3764584](https://github.com/ztalbot2000/homebridge-cmd4/commit/d3764584f5e0faf369490ad85ed16ba3f19e41c8)) * Schema Prep - constants are now lower case and the input is lowered instead ([03b232a1](https://github.com/ztalbot2000/homebridge-cmd4/commit/03b232a152a622025ccfe7c6dc32ab3fa59d6df2)) * New tset tool to check where constant is defined ([4fc3668a](https://github.com/ztalbot2000/homebridge-cmd4/commit/4fc3668ad0e1fe51dde4a32ef83f3c78584f4e91)) * Schema Prep - Change variables to lower case, still check for Upper case ([2b8f4bf7](https://github.com/ztalbot2000/homebridge-cmd4/commit/2b8f4bf768d1c7aebb3c45f6b019de8201c1656e)) * Schema prep- Change constants to an object ([f0dd0de9](https://github.com/ztalbot2000/homebridge-cmd4/commit/f0dd0de923c12fe6dd52cf5469ff87b2d762ee37)) * Continue Schema Prep. Cmd4Storage is now referenced accordingly ([79d85b01](https://github.com/ztalbot2000/homebridge-cmd4/commit/79d85b01bac8d1d3a79a407bcbec0163f19f4e4d)) * Schema Prep. FakeGato simplification ([fd26104b](https://github.com/ztalbot2000/homebridge-cmd4/commit/fd26104b0d9c35a53209e91b3efb9ccf820c0333)) * Prep for Schema. Stored data now version controlled and assoc array class ([59d7a229](https://github.com/ztalbot2000/homebridge-cmd4/commit/59d7a22903fbb95b4e1b55a7fca4629960c89bfe)) * Prep for schema. Warn keys must be lower case ([fe3c155c](https://github.com/ztalbot2000/homebridge-cmd4/commit/fe3c155c5b00574ca3b03baf6d3a32f942756988)) * "Prep for Schema. Unclutter namespace" ([12a63fb5](https://github.com/ztalbot2000/homebridge-cmd4/commit/12a63fb5339f346b31702bc2f52aa60f299c0de4)) ##### Bug Fixes * Unit testing order is relevant. Add --bail for sanityTests ([3d3eb844](https://github.com/ztalbot2000/homebridge-cmd4/commit/3d3eb844499c768efbbfdbda88f2164b276e927e)) * missed with last update ([2282996c](https://github.com/ztalbot2000/homebridge-cmd4/commit/2282996c5056a1b37edc998766e29c90a74f2cfb)) * The warning should read Capitalized, not Upper case ([9b7f7957](https://github.com/ztalbot2000/homebridge-cmd4/commit/9b7f7957613c2c36cb61c86d1f66b3bfa34d49fc)) * Found in demo mode, polling for default characteristics was attempted ([01960b73](https://github.com/ztalbot2000/homebridge-cmd4/commit/01960b73c696d788ea06469c5749da2272662e55)) * For Demo mode, Target states must be defined so warning is not shown ([a1e00e3f](https://github.com/ztalbot2000/homebridge-cmd4/commit/a1e00e3f84e80300c190b50473ee44026bfe42bc)) * Testing Prep Schema fix. Added unit test so will not happen again ([85ab69f7](https://github.com/ztalbot2000/homebridge-cmd4/commit/85ab69f7c366d2ab37ffe76c9eda7ba0d480e3d1)) ## Whats new in 5.1.1 #### 5.1.1 (2021-09-16) ##### Bug Fixes * Bug [#113](https://github.com/ztalbot2000/homebridge-cmd4/pull/113) Add example SecuritySystem script, fixing TargetState ([fc892370](https://github.com/ztalbot2000/homebridge-cmd4/commit/fc89237021e828831c3fef6b340fd9ae0211cfdf)) ## Whats new in 5.1.0 ### 5.1.0 (2021-08-16) ##### Chores * Add PS5.sh as an example script ([9921cd29](https://github.com/ztalbot2000/homebridge-cmd4/commit/9921cd296a040ec2257c867d689e32a2d045cf7b)) ##### New Features * Change process.exit to throw new Error ([0a058100](https://github.com/ztalbot2000/homebridge-cmd4/commit/0a058100b1e7db9a6b94517a9d3de43c6568099f)) * Change process.exit to throw new Error ([203cc67a](https://github.com/ztalbot2000/homebridge-cmd4/commit/203cc67ad9305348e28aaef910966ac08776ff05)) ## Whats new in 5.0.2 #### 5.0.2 (2021-08-14) ##### Bug Fixes * For bug [#112](https://github.com/ztalbot2000/homebridge-cmd4/pull/112) where configured fakegato can cause Cmd4 to not run ([53992de4](https://github.com/ztalbot2000/homebridge-cmd4/commit/53992de45b17aa94c367506868a90965ebb71f0d)) ## Whats new in 5.0.1 #### 5.0.1 (2021-08-09) ##### Bug Fixes * Drastic failure of wormQueue traps on incorrect use of this ([ccaf185d](https://github.com/ztalbot2000/homebridge-cmd4/commit/ccaf185dbe6dded7b2e82c6bde5b6267122b27e5)) ##### Tests * Test drastic failure of WoRm queue ([82b3ab9c](https://github.com/ztalbot2000/homebridge-cmd4/commit/82b3ab9cef9b224d116511246a0b21ac0e8483e1)) ## Whats new in 5.0.0 #### 5.0.0 (2021-08-07) ##### New Features * V5 Optimize features, found some older code still to remove ([c52884b](https://github.com/ztalbot2000/homebridge-cmd4/commit/c52884bee08352723debef95f0c5169ebb8cbabb)) * v5 Simplify, Restart Recover is now always done ([827e8a1](https://github.com/ztalbot2000/homebridge-cmd4/commit/827e8a10763c3ef062f55cb9be108727b0730cc0)) * v5.0 Simplify - Only 1 start polling message ([ac84270](https://github.com/ztalbot2000/homebridge-cmd4/commit/ac842705385ad8d9699cdf98fd84c6737a36c888)) * v5.0 Simplification, Remove unused QUEUEMSG ([f25dac8](https://github.com/ztalbot2000/homebridge-cmd4/commit/f25dac86b42ca603f3c0461455f1b2770b59689d)) * v5.0 Optimize to best config ([120f2a9](https://github.com/ztalbot2000/homebridge-cmd4/commit/120f2a9c3ec2eafb05c051b29e3efe7b1bfdd1f6)) * v5.0 Deprecate Cmd4Mode ([58d8323](https://github.com/ztalbot2000/homebridge-cmd4/commit/58d832301fc58a054646ae87ecf178a5d83aab83)) * v5.0 Optimize ([e41eca2](https://github.com/ztalbot2000/homebridge-cmd4/commit/e41eca2eaaf56a2605f564085f6df11111eecf82)) ## Whats new in 4.2.3 #### 4.2.3 (2021-08-03) ##### Bug Fixes * For WoRm Queue if setValue script fails, lockup can occur ([c3abf782](https://github.com/ztalbot2000/homebridge-cmd4/commit/c3abf7827626cfd89786d767fd153d4f18ea6462)) ## Whats new in 4.2.2 #### 4.2.2 (2021-07-27) ##### Bug Fixes * WoRm queues must act like cached for Aircon ([223c83c7](https://github.com/ztalbot2000/homebridge-cmd4/commit/223c83c7bc4219c54c0f70e6439cd3cc4bb6cf50)) ## Whats new in 4.2.1 #### 4.2.1 (2021-07-25) ##### Bug Fixes * For ticket [#111](https://github.com/ztalbot2000/homebridge-cmd4/pull/111). Oddly node.js is not passing unused arguments in the correct order ([1b50675a](https://github.com/ztalbot2000/homebridge-cmd4/commit/1b50675a4ceebd2fa34ce69b2b4004dba6ae5a25)) ## Whats new in 4.2.0 ## v4.2.0 (2021-07-24) ##### New Features * For Queue (AirCon) ignore low priority polling failure messages ([f431e410](https://github.com/ztalbot2000/homebridge-cmd4/commit/f431e410acefc2025473475c7c986ec27994b4f8)) * Regular polling uses low priority queue, removing duplicate code ([70e53e73](https://github.com/ztalbot2000/homebridge-cmd4/commit/70e53e73fba0a4f5191e850324361b6462c5e33b)) ##### Bug Fixes * For performance Cmd4 was to call the callback to IOS before the Queue ([7d957daf](https://github.com/ztalbot2000/homebridge-cmd4/commit/7d957daf1f4afb9d1fbdb2ad2c81a04f2978f859)) * cross-spawn while maybe faster, fails killing timed out processes ([f12ad71](https://github.com/ztalbot2000/homebridge-cmd4/commit/f12ad7125711952d634b4226c23597d652a97d95)) * Differentiate messages for Queued polling ([fdf6e18](https://github.com/ztalbot2000/homebridge-cmd4/commit/fdf6e186852ad74ed8bb32717de721601000a482)) * Update unit tests for recent changes ([8940795](https://github.com/ztalbot2000/homebridge-cmd4/commit/894079596bace540d174927fd1e593d47f455f99)) * Performance improvement, dont transpose values from homebridge ([c735ef2](https://github.com/ztalbot2000/homebridge-cmd4/commit/c735ef27303aa642ecfe8585d219045e2b53782e)) ##### Testing * Common unit testing messages ([b214fa8](https://github.com/ztalbot2000/homebridge-cmd4/commit/b214fa8cf6bee71bd854dc1f24f901b1dab8e466)) ## Whats new in 4.0.1 #### 4.0.1 (2021-06-25) ##### Enhancements * performance improvement. Cost of every debug log not printed was 300ms ([3c44fab2](https://github.com/ztalbot2000/homebridge-cmd4/commit/3c44fab2800d39deb496cf8d4ad946f3a6368456)) ## Whats new in 4.0.0 ## 4.0.0 (2021-06-24) ##### New Features * Cmd4 gets its own logging mechanism ([048af55c](https://github.com/ztalbot2000/homebridge-cmd4/commit/048af55ca078603889e7d951ff897526d9239179)) ##### Bug Fixes * For slower Aircon Queue, return current stored value and call updateValue later ([45355819](https://github.com/ztalbot2000/homebridge-cmd4/commit/4535581999256447bda1c16bf48f9bd047347ed1)) * if the queue is busy, the next Get entry should not even be tried ([56632c3c](https://github.com/ztalbot2000/homebridge-cmd4/commit/56632c3c4f92530242c83548c95a0d8111964d51)) ## Whats new in 3.10.1 #### 3.10.1 (2021-06-13) ##### Bug Fixes * Queue "Sets" should always come before "Gets" and Replace duplicate Sets ([4da5e323](https://github.com/ztalbot2000/homebridge-cmd4/commit/4da5e323bf4971ec68ee60010b13cedab901cd73)) * All "Gets" should wait until the stateChangeResponseTime in Queues ([9dbfc768](https://github.com/ztalbot2000/homebridge-cmd4/commit/9dbfc76867ac6903a9cf64ad7afa52687d0a7d8a)) ## Whats new in 3.10.0 ### 3.10.0 (2021-05-29) ##### Chores * v3.10.0 packaging ([0a12da5a](https://github.com/ztalbot2000/homebridge-cmd4/commit/0a12da5a8d96dd060665f5ff07a1045eb08a46da)) ##### New Features * Antifeat as queueBurst was a bust. Squash queue errors for 1min instead ([854fec2f](https://github.com/ztalbot2000/homebridge-cmd4/commit/854fec2f9dcbe393f7ab58dfc7ee9d484cb0b7cd)) * Remove feat burst as no added benefit ([afe6b021](https://github.com/ztalbot2000/homebridge-cmd4/commit/afe6b021dd2ecc6564b6396f4368e77836d438de)) ##### Bug Fixes * runtime fix of this ([ce264c37](https://github.com/ztalbot2000/homebridge-cmd4/commit/ce264c374dbf35b9793baa44fd09dfabe30abcc8)) ## Whats new in 3.9.0 ### 3.9.0 (2021-05-26) ##### New Features * Unit test constants defined but not used and used but not defined ([e52fe8d0](https://github.com/ztalbot2000/homebridge-cmd4/commit/e52fe8d0728093644c0e27d25ebdec83625a00fb)) ##### Bug Fixes * Pass down outputConstants like other Cmd4 Directives ([44c44b24](https://github.com/ztalbot2000/homebridge-cmd4/commit/44c44b24fa42404976c9cbfd538e3812f3bf3a7d)) * Possible crash when defining Default Polling before defining the Queue ([e125abab](https://github.com/ztalbot2000/homebridge-cmd4/commit/e125ababe194b2541f25c8f23ac78f4b138db10c)) ## Whats new in 3.8.1 #### 3.8.1 (2021-05-23) ##### Bug Fixes * set to #!/usr/bin/env node for multi platform ([d3544fa1](https://github.com/ztalbot2000/homebridge-cmd4/commit/d3544fa13a9cf654e2b2b36c335ae19c7302a49d)) * First step of multiple related characteristics using an array; no functionality change ([3092e06e](https://github.com/ztalbot2000/homebridge-cmd4/commit/3092e06e1025cf39be924699108fbf17b2d6ff4f)) * Create variable burstMode instead of using burstGroupSize causing issues ([b403f679](https://github.com/ztalbot2000/homebridge-cmd4/commit/b403f679df9ceac72234ba1e0f307697be036b2b)) ##### Tests * Add routine to check for unused constants ([1da024a0](https://github.com/ztalbot2000/homebridge-cmd4/commit/1da024a0dfaab4664384073a90759aab6c3beebb)) ## Whats new in 3.8.0 ### 3.8.0 (2021-05-21) ##### Chores * v3.8.0 packaging ([1cc9006f](https://github.com/ztalbot2000/homebridge-cmd4/commit/1cc9006ff3867cba44c150d87c028b0621f97c62)) ##### Documentation Changes * Document Burst for Queue ([fe057931](https://github.com/ztalbot2000/homebridge-cmd4/commit/fe0579315ea79523fb979b1bec69cfddd71c9e55)) ##### New Features * Add in burst read of Queued Polling ([8b3e8b6e](https://github.com/ztalbot2000/homebridge-cmd4/commit/8b3e8b6eabb07455f2acc90fb6fbe2b0061c79fa)) * rough in quash of error message for Queued Polling ([e1ec4947](https://github.com/ztalbot2000/homebridge-cmd4/commit/e1ec49474fdcda73bad780a2caae91b4ed9bab07)) ##### Bug Fixes * Proper check for burst enabled ([7fab1be6](https://github.com/ztalbot2000/homebridge-cmd4/commit/7fab1be642cd70d28f5433c8b08a7535cb9405ca)) * For burst feat, Dont confuse interval and burstInterval ([59c6490b](https://github.com/ztalbot2000/homebridge-cmd4/commit/59c6490b2c5b4b9ea15ddfa63e1d790b3d3ff9f5)) * Added tests for Queue sanity timer which found stuff ([d1296b13](https://github.com/ztalbot2000/homebridge-cmd4/commit/d1296b13d91e6f7cdd60272ebc327bbe2d9f6e40)) * Change loop condition to bracketed && instead of comma ([3a213e0c](https://github.com/ztalbot2000/homebridge-cmd4/commit/3a213e0c0ef271786080013e3eeddf7a37e74e74)) * From previous rough in commit, fix burstGroupSize ([84e9a3da](https://github.com/ztalbot2000/homebridge-cmd4/commit/84e9a3daf987dc9f0a2eb76492713ab9fc64ab34)) * from previous commit. Adding burst changes the way entry can be parsed ([a1e5ae06](https://github.com/ztalbot2000/homebridge-cmd4/commit/a1e5ae0635dbb50b873033986eabce7920fb0f97)) ## Whats new in 3.7.5 #### 3.7.5 (2021-05-13) ##### Bug Fixes * Queued sanity timer was not what it was supposed to be ([0b9314ab](https://github.com/ztalbot2000/homebridge-cmd4/commit/0b9314aba0495839c365ea817ef20da5d72cd277)) ## Whats new in 3.7.4 #### 3.7.4 (2021-05-12) ##### Bug Fixes * Why does Homebridge check validity of values with invalid return codes affecting Queued polling ([387a13a7](https://github.com/ztalbot2000/homebridge-cmd4/commit/387a13a78483c32e316d4da3dfc187902f6e668e)) ## Whats new in 3.7.3 #### 3.7.3 (2021-05-09) ##### Chores * Update whats in v3.7.3 ([adfe4444](https://github.com/ztalbot2000/homebridge-cmd4/commit/adfe4444bdf349fe20c86dfde9252cd715df6d5b)) * spelling ([394d4d0d](https://github.com/ztalbot2000/homebridge-cmd4/commit/394d4d0d29d773e8e9940618b04ecc62aa2f97d2)) * v3.7.3 packaging ([24cda9d8](https://github.com/ztalbot2000/homebridge-cmd4/commit/24cda9d846384d43fd55a50a70c8fe6b788bd8e9)) ##### New Features * overall performance improvements ([80940ee2](https://github.com/ztalbot2000/homebridge-cmd4/commit/80940ee29bc84ecc1705102b489deec2698a90a0)) ##### Bug Fixes * Enhance polling queue sanity timer. Fix unneeded related queue callback ([b7b35a68](https://github.com/ztalbot2000/homebridge-cmd4/commit/b7b35a68d181f3e5e1458e1ca2f714bb9736014c)) * Related characteristics are Device specific as some are optional in one and required in others ([3e0faf58](https://github.com/ztalbot2000/homebridge-cmd4/commit/3e0faf585032e6d047bd91da2b1dac1453b6cd74)) ## Whats new in 3.7.2 #### 3.7.2 (2021-05-04) ##### Bug Fixes * PriorityPollingQueue (this) corruption ([f51b1976](https://github.com/ztalbot2000/homebridge-cmd4/commit/f51b1976b4e8b42023bf8029fa8ab9fbb275e39b)) ## Whats new in 3.7.1 #### 3.7.1 (2021-05-04) ##### Bug Fixes * branch merge issue. Fix Prio queue timer ([146a558c](https://github.com/ztalbot2000/homebridge-cmd4/commit/146a558c6bd1bbee84ccd7d8c6146ce1e11007e4)) ## Whats new in 3.7.0 ### 3.7.0 (2021-05-03) ##### Documentation Changes * Doc generator for last commit was omitted ([81067fe2](https://github.com/ztalbot2000/homebridge-cmd4/commit/81067fe2d6ed18aa76969c29087c8c469644752e)) * For PriorityQueuePolling documentation of Sequential or WoRm ([4004f02c](https://github.com/ztalbot2000/homebridge-cmd4/commit/4004f02c86a792e578c670885f71a70c6e06da51)) ##### New Features * For PriorityQueuePolling add the config.json implementation structure ([7924ec93](https://github.com/ztalbot2000/homebridge-cmd4/commit/7924ec931a4704bcefec6d9df0687573083ce1b8)) * Add WoRM as PriorityPollingQueue method ([268b44e9](https://github.com/ztalbot2000/homebridge-cmd4/commit/268b44e9ca3b1b54aa14b999f261d81ca3758f52)) ##### Bug Fixes * Sequential Priority Queue Polling fix and add Queue Sanity timer ([f8073610](https://github.com/ztalbot2000/homebridge-cmd4/commit/f80736104a763d95c59a10cb3e0ea22343619056)) * Queue must be working before adding characteristics ([30a5c863](https://github.com/ztalbot2000/homebridge-cmd4/commit/30a5c863d7991e0c79e9ed29b9471367582c96bb)) ## Whats new in 3.6.1 #### 3.6.1 (2021-04-26) ##### Documentation Changes * Add example of Priority Queue Polling accros Accessories ([a901a699](https://github.com/ztalbot2000/homebridge-cmd4/commit/a901a69999a804cbb0550582158f10f44769a454)) ##### Bug Fixes * Incorrect message for staggered polling when also queued polling ([fb020063](https://github.com/ztalbot2000/homebridge-cmd4/commit/fb0200636f2b99b493131cc6b23a83d24df3601c)) * Obey stateChangeResponse time for PriorityQueuePolling. Change to constants as well ([eb94eeb6](https://github.com/ztalbot2000/homebridge-cmd4/commit/eb94eeb6a41ece62ee3b35154633e134235ff976)) ## Whats new in 3.6.0 ### 3.6.0 (2021-04-23) ##### Documentation Changes * Interval and Timeout are hierarchal directives ([2393261c](https://github.com/ztalbot2000/homebridge-cmd4/commit/2393261ce26322eaaca77416e487e3c8e877ae6d)) * Add interval to documentation ([93fec72b](https://github.com/ztalbot2000/homebridge-cmd4/commit/93fec72b2044471212819ba6cfa49ec6943158fb)) * Add missing Cmd4_Mode to platform directives ([685bef51](https://github.com/ztalbot2000/homebridge-cmd4/commit/685bef517229d744c66b1d19d963d0d3d563fce5)) * Add line number of where to specifically look ([e4829fd8](https://github.com/ztalbot2000/homebridge-cmd4/commit/e4829fd8a74d96f248f53d2244ba297dce45d4a1)) * Adjust docs for Custom Characteristic ([b39f8b7f](https://github.com/ztalbot2000/homebridge-cmd4/commit/b39f8b7f4ba9da1d42d2f62b2efedc4bcf456447)) * Documentation for custom characteristic feature previously submitted ([6f9ebb49](https://github.com/ztalbot2000/homebridge-cmd4/commit/6f9ebb49100ab3b82c62e7c467037333a875f1df)) ##### New Features * Priority Polling Queue CLI, doc & tests ([c6c7fa36](https://github.com/ztalbot2000/homebridge-cmd4/commit/c6c7fa362d92c5c8dc90ef05e9ae467b5e513f82)) * Performance stats for Priority Queued Polling ([a9850885](https://github.com/ztalbot2000/homebridge-cmd4/commit/a98508859e02e59cee536780693f2483ae6f0e88)) * Create priority polling queue so there are no collisions ([b135ef79](https://github.com/ztalbot2000/homebridge-cmd4/commit/b135ef7985480575412a3dca2f7185a4b5392631)) ##### Tests * Message order change ([5f330170](https://github.com/ztalbot2000/homebridge-cmd4/commit/5f330170a8641d68c366c3d4ebaa32ce787d711c)) * Add more code coverage around polling ([50b102af](https://github.com/ztalbot2000/homebridge-cmd4/commit/50b102afdf4983514ae0a16757e243d8901a6b08)) * Resolved still more tests that disrupted others ([7669d883](https://github.com/ztalbot2000/homebridge-cmd4/commit/7669d88366e12ee6a9114a2a813671473342dabc)) * Figured out how to add back in conflicting tests ([413f5a87](https://github.com/ztalbot2000/homebridge-cmd4/commit/413f5a87d2c4e1813d04d6f7da81540660af38c1)) * Add in more polling tests ([91b56817](https://github.com/ztalbot2000/homebridge-cmd4/commit/91b568175c044adddf3f55b2715ddb89a9de4dd2)) ## Whats new in 3.5.1 #### 3.5.1 (2021-04-15) ##### Bug Fixes * For bug [#106](https://github.com/ztalbot2000/homebridge-cmd4/pull/106) polling was not able to go lower than 6 seconds ([51459211](https://github.com/ztalbot2000/homebridge-cmd4/commit/5145921132d64483d686175c9d6a316b7186d626)) ## Whats new in 3.5.0 ### 3.5.0 (2021-04-14) ##### New Features * Add custom characteristics ([e3c07d55](https://github.com/ztalbot2000/homebridge-cmd4/commit/e3c07d55ca7e73ed630b9ab072b3f0ec36a1b269)) ## Whats new in 3.4.4 #### 3.4.4 (2021-04-09) ##### Bug Fixes * [#104](https://github.com/ztalbot2000/homebridge-cmd4/pull/104). One day one bug, uncovered another ([9075bfe3](https://github.com/ztalbot2000/homebridge-cmd4/commit/9075bfe326a2561e9d511ff4bdc662767e953189)) ## Whats new in 3.4.3 #### 3.4.3 (2021-04-08) ##### Bug Fixes * Polling has always used the wrong timeout ([74f1f908](https://github.com/ztalbot2000/homebridge-cmd4/commit/74f1f908ca3ab847ca37de2dd4b77f6b11e373c2)) ##### Tests * Prep for using our own Logger in other than test ([8c4042a1](https://github.com/ztalbot2000/homebridge-cmd4/commit/8c4042a1c9b6d6f52e4699dfc67c19c19d6bbe9a)) ## Whats new in 3.4.2 #### 3.4.2 (2021-04-05) ##### New Features * Only add new Cmd4 directives for feature in progress ([438462e0](https://github.com/ztalbot2000/homebridge-cmd4/commit/438462e0148d6b96f7da7d6726242c1f1194eed3)) ##### Tests * Better technique for capturing logging during Unit Testing ([ee2c7dd3](https://github.com/ztalbot2000/homebridge-cmd4/commit/ee2c7dd3355785af35a8bb6221724cc6f5947cbb)) ## Whats new in 3.4.1 #### 3.4.1 (2021-03-26) ##### Bug Fixes * for bug[#102](https://github.com/ztalbot2000/homebridge-cmd4/pull/102). Fumble fingers causing setCachedValue being called, instead of setValue ([b7ff2f68](https://github.com/ztalbot2000/homebridge-cmd4/commit/b7ff2f68c4d4f0e78c02e478fc43e6e2f81bfe35)) ## Whats new in 3.4.0 ### 3.4.0 (2021-03-25) ##### Bug Fixes * Resolve state being changed over restart ([910f44db](https://github.com/ztalbot2000/homebridge-cmd4/commit/910f44db78680c82892cfa5c163c00e0165ffb56)) * Show only start and end of polling kick off message ([7ba5dde9](https://github.com/ztalbot2000/homebridge-cmd4/commit/7ba5dde99924890c78b2ed864b4785f817831b5e)) ##### New Features * Try to correct meaning of "fetch" by changing it to Cmd4_Mode ([3d47291e](https://github.com/ztalbot2000/homebridge-cmd4/commit/3d47291eb7e7dc5fa39440ba5f7155c8a19e462f)) ## Whats new in 3.3.2 #### 3.3.2 (2021-03-22) ##### New Features * first part of description for config.schema.json, otherwise unused ([c4f60ded](https://github.com/ztalbot2000/homebridge-cmd4/commit/c4f60dedccd1b8694f9e4687f1135d7a14404f22)) * Further enhancements to polling ([ff559fc8](https://github.com/ztalbot2000/homebridge-cmd4/commit/ff559fc8d82ece0b0a9892d72877b7ddae3ea9b8)) ##### Bug Fixes * fakegato humidity for eve room being undefined ([4fb9f7ea](https://github.com/ztalbot2000/homebridge-cmd4/commit/4fb9f7eaabf7b2232d0f32d18490eb1cc6f192ab)) * Do not respond until after related characteristic fetch, resolve possible polling collisions ([0b3ff3ca](https://github.com/ztalbot2000/homebridge-cmd4/commit/0b3ff3ca4c0b296b052a0cd1021583d8f6da419f)) ## Whats new in 3.3.1 #### 3.3.1 (2021-03-15) ##### Bug Fixes * Not handling too much data well ([b4fd8a19](https://github.com/ztalbot2000/homebridge-cmd4/commit/b4fd8a1935817c011b34469fd4505c1cd3294c97)) ## Whats new in 3.3.0 ### 3.3.0 (2021-03-13) ##### Chores * packaging for 3.2.5-beta6 ([5ef6d0e8](https://github.com/ztalbot2000/homebridge-cmd4/commit/5ef6d0e8e4d29679e1550b69489aefbdb318d163)) * Missing file ([b95bb22e](https://github.com/ztalbot2000/homebridge-cmd4/commit/b95bb22e46121e36a4dc9dd741ad9a43e24491b9)) ##### New Features * Now that polling uses updateValue, polling can be independent of fetch ([b3ca7f0](https://github.com/ztalbot2000/homebridge-cmd4/commit/b48d88b66ef1a715d347c6a712aee0a927f48741)) * Change to RUNNING_CHANGLOG for Homebeidge-UI bug that cannot follow inline URLs ([13e57946](https://github.com/ztalbot2000/homebridge-cmd4/commit/13e57946c187424f8bef4deb8b28860ba3276330)) ##### Bug Fixes * properly handle removed platform accessories from cache ([2ce2e9a3](https://github.com/ztalbot2000/homebridge-cmd4/commit/2ce2e9a31030e1f931bc28ae2c55eca59d0c8899)) * Globally allow statusMsg option to be set ([cc173514](https://github.com/ztalbot2000/homebridge-cmd4/commit/cc17351407005926a9c3b218f889727b1fb852ef)) ##### Tests * Test all constants, just in case accidentally changed ([b3ca7f07](https://github.com/ztalbot2000/homebridge-cmd4/commit/b3ca7f07a20d25750e10dee5e0b533dd6f7393fb)) ## Whats new in 3.2.5-beta6 #### 3.2.5-beta6 (2021-03-12) ##### Chores * Missing file ([b95bb22e](https://github.com/ztalbot2000/homebridge-cmd4/commit/b95bb22e46121e36a4dc9dd741ad9a43e24491b9)) ##### New Features * Change to RUNNING_CHANGLOG for Homebeidge-UI bug that cannot follow inline URLs ([13e57946](https://github.com/ztalbot2000/homebridge-cmd4/commit/13e57946c187424f8bef4deb8b28860ba3276330)) ##### Bug Fixes * properly handle removed platform accessories from cache ([2ce2e9a3](https://github.com/ztalbot2000/homebridge-cmd4/commit/2ce2e9a31030e1f931bc28ae2c55eca59d0c8899)) * Globally allow statusMsg option to be set ([cc173514](https://github.com/ztalbot2000/homebridge-cmd4/commit/cc17351407005926a9c3b218f889727b1fb852ef)) ## Whats new in 3.2.5-beta5 #### 3.2.5-beta5 (2021-03-11) ##### New Features * Polling now calls updateValue ([9c75b069](https://github.com/ztalbot2000/homebridge-cmd4/commit/9c75b0692ea687559a112269bdfa09fef5568e2e)) * Double check for polling or services not being created twice ([2d856289](https://github.com/ztalbot2000/homebridge-cmd4/commit/2d85628996fc3702b5b309754a6bde25dc95f605)) ##### Bug Fixes * lower Temperature in config.json to match homebridges expected values without prop changes ([78cf3ea7](https://github.com/ztalbot2000/homebridge-cmd4/commit/78cf3ea7f4cae2438023888bf1084d8f649e7a20)) * transposing constants to strings and add subType to clarify accessory services ([99d69cec](https://github.com/ztalbot2000/homebridge-cmd4/commit/99d69cec5eb1902f0429a6a84276571fbe6e298a)) ##### Other Changes * Always ([1e144e9c](https://github.com/ztalbot2000/homebridge-cmd4/commit/1e144e9c027f3b0dc353b0bd1c2edd2819712654)) ## Whats new in 3.2.5-beta3 #### 3.2.5-beta3 (2021-03-07) ##### Chores * packaging of 3.2.5-beta3 ([0686607b](https://github.com/ztalbot2000/homebridge-cmd4/commit/0686607b3fff0028054021bcf16c2917b242ae3e)) ##### Other Changes * Stagger Polling Starts ([1e144e9c](https://github.com/ztalbot2000/homebridge-cmd4/commit/1e144e9c027f3b0dc353b0bd1c2edd2819712654)) ## Whats new in 3.2.5-beta2 #### 3.2.5-beta2 (2021-03-05) ##### Bug Fixes * use cross spawn instead of exec ([ab51b8dd](https://github.com/ztalbot2000/homebridge-cmd4/commit/ab51b8dd9b9a543d222d22f54348b06ff901d011)) ## Whats new in 3.2.5-beta1 #### 3.2.5-beta1 (2021-03-03) ##### Chores * packaging for v3.2.3 ([504d2e23](https://github.com/ztalbot2000/homebridge-cmd4/commit/504d2e23cd1adfb6713dcdc74cb675a896b93cc9)) ##### Documentation Changes * Homebridge-UI-X intercept message ([06858799](https://github.com/ztalbot2000/homebridge-cmd4/commit/06858799144b8066762925ba101f16824ce48749)) ##### Bug Fixes * improve performance of getValue by pre-dfining conversion func based on char being fetched ([51509d6d](https://github.com/ztalbot2000/homebridge-cmd4/commit/51509d6d464d5c8a38c444dbf70acdb5526cafa2)) * resolve warnings for strings that could validly have multiple words ([c6642b6c](https://github.com/ztalbot2000/homebridge-cmd4/commit/c6642b6c519e81d5a3bbadcf503f3c4d1eb33569)) * [#97](https://github.com/ztalbot2000/homebridge-cmd4/pull/97) Handle both cases for input in config.json. Default should have been UC ([d105cdfe](https://github.com/ztalbot2000/homebridge-cmd4/commit/d105cdfe5848e533f1a0b8a56075e028161baf76)) * For [#96](https://github.com/ztalbot2000/homebridge-cmd4/pull/96) Warning message is for related device characteristics only ([f2c28f1a](https://github.com/ztalbot2000/homebridge-cmd4/commit/f2c28f1a46fad0baf073b47f1ccf4612a85c4eb2)) ##### Code Style Changes * remove dead code ([a3d42a8d](https://github.com/ztalbot2000/homebridge-cmd4/commit/a3d42a8dfcac5263388106d6caf312df22980bb5)) * It seems passing structure is expensive ([0b7b4253](https://github.com/ztalbot2000/homebridge-cmd4/commit/0b7b425398bd0e081e2c5faadaefc7d686587da0)) ##### Tests * Add full initPlugin test, cannot run with others so commented out ([a6de84ac](https://github.com/ztalbot2000/homebridge-cmd4/commit/a6de84ac6f0bd9c13ccbb9b796e1d4c1a1fc3745)) * fix unit test that was incomplete ([8c0e2dd0](https://github.com/ztalbot2000/homebridge-cmd4/commit/8c0e2dd0e5f78ab06c5726b89f326c777022d728)) ## Whats new in 3.2.3 #### 3.2.3 (2021-03-01) ##### Documentation Changes * Homebridge-UI-X intercept message ([06858799](https://github.com/ztalbot2000/homebridge-cmd4/commit/06858799144b8066762925ba101f16824ce48749)) ##### Bug Fixes * resolve warnings for strings that could validly have multiple words ([c6642b6c](https://github.com/ztalbot2000/homebridge-cmd4/commit/c6642b6c519e81d5a3bbadcf503f3c4d1eb33569)) * [#97](https://github.com/ztalbot2000/homebridge-cmd4/pull/97) Handle both cases for input in config.json. Default should have been UC ([d105cdfe](https://github.com/ztalbot2000/homebridge-cmd4/commit/d105cdfe5848e533f1a0b8a56075e028161baf76)) * For [#96](https://github.com/ztalbot2000/homebridge-cmd4/pull/96) Warning message is for related device characteristics only ([f2c28f1a](https://github.com/ztalbot2000/homebridge-cmd4/commit/f2c28f1a46fad0baf073b47f1ccf4612a85c4eb2)) ##### Tests * Add full initPlugin test, cannot run with others so commented out ([a6de84ac](https://github.com/ztalbot2000/homebridge-cmd4/commit/a6de84ac6f0bd9c13ccbb9b796e1d4c1a1fc3745)) * fix unit test that was incomplete ([8c0e2dd0](https://github.com/ztalbot2000/homebridge-cmd4/commit/8c0e2dd0e5f78ab06c5726b89f326c777022d728)) ## Whats new in 3.2.2 #### 3.2.2 (2021-02-26) ##### Bug Fixes * For [#95](https://github.com/ztalbot2000/homebridge-cmd4/pull/95) correct link to pull requests ([6547ff8b](https://github.com/ztalbot2000/homebridge-cmd4/commit/6547ff8be33cb40041d03bd40f523bdc69f7d549)) * [#94](https://github.com/ztalbot2000/homebridge-cmd4/pull/94) Changes affect unit testing and Auto generated docs ([876f9889](https://github.com/ztalbot2000/homebridge-cmd4/commit/876f9889c7c46575d0d207a384469389aacc6cbe)) ## Whats new in 3.2.1 #### 3.2.1 (2021-02-24) ##### Bug Fixes * resolve setting Target characteristics that do not set thier Current characteristic counterpart ([278ffcc7](https://github.com/ztalbot2000/homebridge-cmd4/commit/278ffcc7f34fbd7b648971044a05dbad01189b36)) * [#90](https://github.com/ztalbot2000/homebridge-cmd4/pull/90) For Config-UI-X not displaying markdown properly. change to HTML anchors with base URL ([0b8cfef0](https://github.com/ztalbot2000/homebridge-cmd4/commit/0b8cfef05f0d2be0b2baaaad1aa106043dd6d0f3)) ##### Code Style Changes * lint auto doc generator ([bd259c6f](https://github.com/ztalbot2000/homebridge-cmd4/commit/bd259c6f36231860b2ecebe52f780aeaed9d9cd0)) ##### Tests * add markdown checker ([0dd19ca7](https://github.com/ztalbot2000/homebridge-cmd4/commit/0dd19ca7f5588390ec5a904a75199b4817f1353e)) ## Whats new in 3.2.0 ### 3.2.0 (2021-02-23) ##### Bug Fixes * cached data over restarts ([020797fe](https://github.com/ztalbot2000/homebridge-cmd4/commit/020797fe70cd82afaca5a0c5d713cdab1ca5b32a)) ## Whats new in 3.1.3 #### 3.1.3 (2021-02-17) ##### Chores * Path for my m1mini changed ([81d5d7d0](https://github.com/ztalbot2000/homebridge-cmd4/commit/81d5d7d0da1c03eb5da03b3e086f6a4170034dd5)) ##### Documentation Changes * Correct link for case change ([249c31f1](https://github.com/ztalbot2000/homebridge-cmd4/commit/249c31f188b55755644031c6d61d5aa79e149108)) ##### Bug Fixes * Setting cached values should set their corresponding cached verify characteristic ([b8b34098](https://github.com/ztalbot2000/homebridge-cmd4/commit/b8b340986029ff11f3523c6b8b29fd1e8859b73c)) * Any stderr messages should be logged ([b6289133](https://github.com/ztalbot2000/homebridge-cmd4/commit/b6289133d4ceb6fd3f6c1744670df66741cf4ea2)) ##### Tests * Check cached values should set their corresponding cached verify characteristic ([a9b380a5](https://github.com/ztalbot2000/homebridge-cmd4/commit/a9b380a582819c59df30d7ec0f57c7669c99844a)) * Check for stderr messages being logged ([442e6beb](https://github.com/ztalbot2000/homebridge-cmd4/commit/442e6beb463f56f11107518a779bf48e83c8bc64)) ## Whats new in 3.1.2 #### 3.1.2 (2021-02-15) ##### Bug Fixes * A bit better answer to [#89](https://github.com/ztalbot2000/homebridge-cmd4/pull/89), with testcases to prove it ([569018b5](https://github.com/ztalbot2000/homebridge-cmd4/commit/569018b5a0f6bfc76b045d70acead590fa267de2)) ## Whats new in 3.1.1 #### 3.1.1 (2021-02-15) ##### Bug Fixes * For [#89](https://github.com/ztalbot2000/homebridge-cmd4/pull/89). No returned getValue results in "Cannot read property reduce of null" ([3d080d3d](https://github.com/ztalbot2000/homebridge-cmd4/commit/3d080d3dc85754ff9eac030507b5322f5faad5e9)) ## Whats new in 3.1.0 ### 3.1.0 (2021-02-14) ##### Features * For #88 Log a Status change message and have it be optional statusMsg ([019c6d2](https://github.com/ztalbot2000/homebridge-cmd4/commit/ef9f57fc646eeab5a15e404f445545a2a2528391)) ##### Build System / Dependencies * Remove images destined for github pages from npm distribution ([7a11296b](https://github.com/ztalbot2000/homebridge-cmd4/commit/7a11296b783e3d51237f9109b62696f684f0d902)) * Remove images destined for github pages from npm distribution ([7a11296b](https://github.com/ztalbot2000/homebridge-cmd4/commit/7a11296b783e3d51237f9109b62696f684f0d902)) * Add commit message rules and up version generator ([d6dfcb19](https://github.com/ztalbot2000/homebridge-cmd4/commit/d6dfcb19bd98e782ac103dcecd5b0e0dcce37a07)) * npm shoud ignore .github templates etc ... ([a457e0bc](https://github.com/ztalbot2000/homebridge-cmd4/commit/a457e0bc92b59e983ba33e8d11fb07207084d480)) ##### Chores * Already merged. Try to fix Git tree ([33694479](https://github.com/ztalbot2000/homebridge-cmd4/commit/3369447934e2f328f2a69d37cf61ceedc97f6f63)) ##### Bug Fixes * Store value to cache after successful set to device when fetch="Polled" ([18a4f569](https://github.com/ztalbot2000/homebridge-cmd4/commit/18a4f5691fe67abc3c64d152822134b77b09aaa4)) * Cached characteristics were never stored if changed by other than polling ([fd3b4cc8](https://github.com/ztalbot2000/homebridge-cmd4/commit/fd3b4cc894598bf424371de03656054d713f3663)) ##### Other Changes * < true/false > ([ef9f57fc](https://github.com/ztalbot2000/homebridge-cmd4/commit/ef9f57fc646eeab5a15e404f445545a2a2528391)) * Update issue templates ([5eb330ad](https://github.com/ztalbot2000/homebridge-cmd4/commit/5eb330ad18a58f8c5f1a456ed261bb6c963c8217)) * Update issue templates ([f19bec63](https://github.com/ztalbot2000/homebridge-cmd4/commit/f19bec6322f77026c0da707a5b283fbe956beb6f)) * Rename Changelog.md to CHANGELOG.md for naming conventions and future use of standard-version ([c9a4b746](https://github.com/ztalbot2000/homebridge-cmd4/commit/c9a4b746aea2c9b003b4219ab4d042cc7b3eadbe)) * Add in Fetch Diagram ([7bd8b46e](https://github.com/ztalbot2000/homebridge-cmd4/commit/7bd8b46eecb62c232137dfba6c1356d7c619bf01)) * Fixed broken link to Developers Guide. ([a55e6235](https://github.com/ztalbot2000/homebridge-cmd4/commit/a55e6235c036992c2dd6fb6879f82635fe5a3d03)) * Fix broken link to LICENSE. ([9b702d0a](https://github.com/ztalbot2000/homebridge-cmd4/commit/9b702d0a704d19c4aae764003e527d47013663df)) * Fix links to 'raw' text versions of links. ([33e558b4](https://github.com/ztalbot2000/homebridge-cmd4/commit/33e558b41726dc5fb518933dd993387e0fe17e59)) * Fix broken link to LICENSE. ([82ab2aba](https://github.com/ztalbot2000/homebridge-cmd4/commit/82ab2aba3fb16cc6992fbc2c0fc1a0a0dec3cae6)) * Fixed broken link to Developers Guide ([f46b2b78](https://github.com/ztalbot2000/homebridge-cmd4/commit/f46b2b7873bbd89c7780c58b2916fc28b41e4873)) * Create issue templates. ([5059486f](https://github.com/ztalbot2000/homebridge-cmd4/commit/5059486fc65ef39e8aa7aed02c093f6fd95e0537)) ##### Tests * fix tests that sometimes failed ([cd005d9b](https://github.com/ztalbot2000/homebridge-cmd4/commit/cd005d9b344aee92a03127c19947f4ba47f11eeb)) * Check if 0, "0" or "0.0" is handled correctly and it is ([019c6d26](https://github.com/ztalbot2000/homebridge-cmd4/commit/019c6d267fbe73a36099806eb79d4902e6698907)) * Add test for same config as polling ([4eb8c09a](https://github.com/ztalbot2000/homebridge-cmd4/commit/4eb8c09aed103edbf5d05d78269fc0835f198477)) ## Whats new in 3.0.15 * Cached characteristics were never stored when not updated by other than polling. ## Whats new in 3.0.14 * Remove unwanted log. * Code cleanup of setValue. Stuff no longer relavent. ## Whats new in 3.0.12 * Absolutely no changes. Advertise deprecation notice of State.js and new Cmd4 GitHub Pages at: https://ztalbot2000.github.io/homebridge-cmd4 ## Whats new in 3.0.11 * Fix for output constants occuring if not default not set. ## Whats new in 3.0.9 * Fix removal of fetch, causing caching of the designation to result in "Invalid value: undefined for Fetch" ## Whats new in 3.0.8 * Fix outputConstants not outputting anything if no translation * Fix debug message causing trap during outputConstants * Beginning of auto generated documentation in GitHub. Please Do not criticize yet. Work in progress. ## Whats new in 3.0.7 * Fix Switch on/off being off/on ## Whats new in 3.0.6 * No changes, just packaging issue. ## Whats new in 3.0.5 * Fix of issue #78. Most constants were not documented correctly and 3.0 no longer uppercased them for you. Put back this capability and fix the documentation. * Fix Bug: ReferenceError: Cannot access 'result' before initialization when Fetch is set to Cached and some other details. ## Whats new in 3.0.4 * BugFix of issue #76 for those who did not specify any polling type at all and "fetch": "Always" ( The default ) caused any characteristic state to be retrieved at all. * Minor improvement to the version checker that stopped post install notes from being displayed. * Allow Cmd4 cached directive changes, like "polling" to be overwritten over restarts. ## Whats new in 3.0.0 ### Changes to support IOS 14. * Cmd4 now follows the [Homebridge Plugin Template](https://github.com/homebridge/homebridge-plugin-template). This means that: * Accessories[] are now platformAccessories and can be published externally with an optional category. i.e.
```json { "category": "TELEVISION" } ``` * This allows multiple TV's to be configured per Bridge as separate platforms. * Accessory plugins can also be defined separately. * This fixes TV icons not being displayed in the Home App of IOS 14. * Number of Devices increased from 52 to 64. * Number of Characteristics increased from 160 to 221. * Returned "quoted strings" or 'quoted strings' are now acceptable. * Reduced child processes with new "fetch" option * { "fetch": "Always" } - As before ( Default ) * { "fetch": "Cached" } - Never fetch characteristic value. Use cached value. The cached value would have to be updated through polling. * { "fetch": "Polled" } - Polled characteristics act like before, "Always". Non polled characteristic values are fetched from cache. * Added the ability to remember characteristic values over restarts. This also means that device name changes and the like are possible. Disabled by:
```json "restartRecover": false ``` In the section of the config.json where "platform": "Cmd4" is defined. Default is true. * Retrieved characteristic values are now converted to those defined by homebridge, instead of guessed by their possible types. * Accessory polling definition changes from:
``` [{ < characteristic >:< defaultValue >, "interval":< sec >, "timeout": < msec > }] ``` To:
``` [ "characteristic": < characteristic >, "interval":< sec >, "timeout": < msec > }] ``` Note: Old style is still supported with warning. * Moved some functions to utilities for easier unit testing. * Splitting documentation into three different areas, README, Advanced Troubleshooting and a Developers guide. * Changed utility colors.js for package chalk. * Internally, any string is now a constant, limiting some possible typos. * Added eslint and resolved all recommended lint errors. # Important While this version appears backward compatible, there is a difference in that the Accessories are created as Platform Accessories as compared to Standard Accessories. The [Homebridge API](https://developers.homebridge.io/#/) documentation details the difference as does the new [Cmd4 Developers Guide](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/docs/Developers.md).

## Whats new in 2.4.2 * Bug fix for negative temperature values * Add in ability to change properties like minValue. Effect is none as not used, except for further testing.
## Whats new in 2.4.1 * Add check for duplicate UUID's that may be created and homebridge now complains about. * Fix issue were configuredName was not provided in users config.json. * Add an example of a DoorLock.sh with on a PI and using a GPIO to control it. * Add a tool to me debug users config.json files. ## Whats new in 2.4.0 * While a bump to the minor release, the changes are development only and also for any spelling errors. * The plugin was split into its respective program/lib/utility/ChangeLog modules. * Splitting the plugin allowed for better Unit testing. * What is new are some examples of scripts for ping and wake on lan and can be found in the Extras/CMD4Scripts/Examples sub-directory. ## Whats new in 2.3.2 * Minor changes for spelling and package updates. Nothing to see here. These are not the changes you are looking for.
## Whats new in 2.3.1 * Minor changes for spelling and package updates. Nothing to see here. These are not the changes you are looking for.
* Updates for Homebridge 1.0.4. * Added 'AUTO' as current heating state matching Apples Development documentation. * Increased default timeout to 8 seconds as HomeBridge 1.04 seems slower.
* Pull in README update noted by Daniel. ## Whats new in 2.3.0 * Added state_cmd_prefix and state_cmd_suffix to possible state_cmd options. This does what they intend to where:
      state_cmd_prefix - adds a string before the state_cmd. i.e. state_cmd_prefix=sudo would create the Set command to be:
         sudo node .homebridge/Cmd4Scripts/State.js 'My_Fan' '1'
      state_cmd_suffix - adds a string after the state_cmd. i.e. state_cmd_suffix="00:16:AA:BB:1F:2D" would create the Set command to be:
         node .homebridge/Cmd4Scripts/State.js Set 'My_Fan' '1' 00:16:AA:BB:1F:2D
    Combining the two i.e. state_cmd_prefix=sudo state_cmd_suffix="00:16:AA:BB:1F:2D" would create the Get command to be:
         sudo node .homebridge/Cmd4Scripts/State.js Get 'My_Fan' 00:16:AA:BB:1F:2D
## Whats new in 2.2.5 * There is a bug in github-version-checker. I can't get around it so change to a direct comparison of what is in npm. ## Whats new in 2.2.2 * This minor version adds an error message when accidentally defining multiple characteristics for characteristic polling. ## Whats new in 2.2.1 * This minor version differentiates the optional characteristic 'Name' from displayName. displayName is used when creating the service. It is essentially the same thing, but this follows the spec exactly. You do not need to change your config.json file if you do not want too. * Internally all properties of all characteristics are defined. * Homebridge does not allow you to getCharacteristic information easily. This allows Cmd4 to stop you from using characteristics with a format of TLV8 that causes HomeBridge to fail to start. This is why the new option:'allowTLV8' was created and set to false by default. Again just ignore it. * With all the properties defined, Test cases increases to 7644. ## Whats new in 2.2 * This version adds in linked accessories. HDMI Input sources for a TV are now shown in HomeKit as an example. * This release also defines constants for all the characteristics that has them. Look at the config.min.json file for descriptions of the constants per characteristic. * The number of test cases increases because of constants to 4500. That equates to almost 61\% more coverage, and less possible errors. * Finally this release adds in version checking so you are always up to date with the latest version of Homebridge-Cmd4. ## Whats new in 2.1 * This minor release fixes duplicate service calls for initializing an AccessoryInformation device, Television & TelevisionSpeaker. Mostly importantly it wipes out 5600 duplicate lines of code to one bound function. ## Whats new in 2.0 * 2This release changes the philosophy of homebridge-Cmd4 from Accessories that have known characteristics as per the HAP Spec to assigning any characteristic to any Accessory. There are 160 possible characteristics, 60 more than the previous version. Also TV, TV Speaker, Input Source, Irrigation, Faucets and many other accessories are now available. ================================================ FILE: cmd4Constants.js ================================================ "use strict"; // Naming convention // DEFAULT_ => Default values // _l => Lower Case // _lv => Lower Case Variable of same name // Triggers which Array CMD4Accessory will be placed // Either cmd4Accessories or cmd4StandaloneAccessories // var cmd4Constants = { STANDALONE: "Standalone", PLATFORM: "Platform", // Default intervals // 10 seconds SLOW_STATE_CHANGE_RESPONSE_TIME: 10000, // 3 seconds MEDIUM_STATE_CHANGE_RESPONSE_TIME: 3000, // 3 seconds DEFAULT_STATE_CHANGE_RESPONSE_TIME: 3000, // 1 second FAST_STATE_CHANGE_RESPONSE_TIME: 1000, // 1 minute DEFAULT_INTERVAL: 60000, // 1 minute DEFAULT_TIMEOUT: 60000, // Not a Bool, otherwise conditional check fails DEFAULT_STATUSMSG: "TRUE", DEFAULT_QUEUE_TYPE: "WoRm", // 10 seconds DEFAULT_QUEUE_PAUSE_TIMEOUT: 10000, // No debug DEFAULT_DEBUG: false, // No funny TLV8 characteristics DEFAULT_ALLOW_TLV8: false, DEFAULT_OUTPUTCONSTANTS: false, // Fakegato Constants FAKEGATO_TYPE_ENERGY: "energy", FAKEGATO_TYPE_ROOM: "room", FAKEGATO_TYPE_WEATHER: "weather", FAKEGATO_TYPE_DOOR: "door", FAKEGATO_TYPE_MOTION: "motion", FAKEGATO_TYPE_THERMO: "thermo", FAKEGATO_TYPE_AQUA: "aqua", EVE: "eve", STORAGE: "storage", STORAGEPATH: "storagePath", FOLDER: "folder", KEYPATH: "keyPath", STATUS: "status", TEMP: "temp", SETTEMP: "setTemp", HUMIDITY: "humidity", PPM: "ppm", POWER: "power", PRESSURE: "pressure", CURRENTTEMP: "currentTemp", VALVEPOSITION: "valvePosition", WATERAMOUNT: "waterAmount", TIME: "time", PATH: "path", FS: "fs", GOOGLE_DRIVE: "googleDrive", // Config Constants DEBUG: "debug", OUTPUTCONSTANTS: "outputConstants", STATUSMSG: "statusMsg", QUEUETYPE: "queueType", QUEUETYPES: "queueTypes", // Queue Types QUEUETYPE_SEQUENTIAL: "Sequential", QUEUETYPE_WORM: "WoRm", QUEUETYPE_WORM2: "WoRm2", // Used internally to mean only polled entries go straight through the queue QUEUETYPE_STANDARD: "StandarD", // Used internally to mean entries go straight through the queue QUEUETYPE_PASSTHRU: "None", DEFAULT_STANDARD_QUEUE_RETRY_COUNT: 0, DEFAULT_WORM_QUEUE_RETRY_COUNT: 0, QUEUE_RETRIES: "retries", // Platform/Accessory Config Constants TYPE: "type", SUBTYPE: "subType", DISPLAYNAME: "displayName", UUID: "uuid", ACCESSORY: "accessory", CATEGORY: "category", PUBLISHEXTERNALLY: "publishExternally", CHARACTERISTIC: "characteristic", TIMEOUT: "timeout", QUEUE: "queue", POLLING: "polling", INTERVAL: "interval", STATECHANGERESPONSETIME: "stateChangeResponseTime", STATE_CMD_PREFIX: "state_cmd_prefix", STATE_CMD_SUFFIX: "state_cmd_suffix", STATE_CMD: "state_cmd", FAKEGATO: "fakegato", REQUIRES: "requires", CONSTANTS: "constants", VARIABLES: "variables", LINKEDTYPES: "linkedTypes", ACCESSORIES: "accessories", URL: "url", ALLOWTLV8: "allowTLV8", DEFINITIONS: "definitions", PROPS: "props", // While also characteristics, they are also used by // the infomation service MANUFACTURER: "manufacturer", SERIALNUMBER: "serialNumber", MODEL: "model", // Internal list variables ACCESSORY_lv: "accessory", CHARACTERISTIC_STRING_lv: "characteristicString", CALLBACK_lv: "callback", ACC_TYPE_ENUM_INDEX_lv: "accTypeEnumIndex", INTERVAL_lv: "interval", IS_SET_lv: "isSet", QUEUE_NAME_lv: "queueName", QUEUE_GET_IS_UPDATE_lv: "queueGetIsUpdate", STATE_CHANGE_RESPONSE_TIME_lv: "stateChangeResponseTime", TIMEOUT_lv: "timeout", VALUE_lv: "value", CMD4_STORAGE_lv: "cmd4Storage", ERROR_STRING_MIN: -151, ERROR_TIMER_EXPIRED: -151, // ERROR_CMD_FAILED_REPLY: -152, ERROR_NULL_REPLY: -153, ERROR_NULL_STRING_REPLY: -154, ERROR_EMPTY_STRING_REPLY: -155, ERROR_2ND_NULL_STRING_REPLY: -156, ERROR_NON_CONVERTABLE_REPLY: -157, ERROR_NO_DATA_REPLY: -158, ERROR_STRING_MAX: -158, ERROR_STRINGS: [ // cmd4Constants.ERROR_TIMER_EXPIRED -151 "Timer expired contacting accessory", // cmd4Constants.ERROR_CMD_FAILED_REPLY -152 "Command failed", // cmd4Constants.ERROR_NULL_REPLY -153 "Reply is NULL", // cmd4Constants.ERROR_NULL_STRING_REPLY -154 "Reply is NULL string", // cmd4Constants.ERROR_EMPTY_STRING_REPLY -155 "Reply is an empty string", // cmd4Constants.ERROR_2ND_NULL_STRING_REPLY -156 "Reply is still NULL", // cmd4Constants.ERROR_NON_CONVERTABLE_REPLY -157 "Cannot convert characteristic string", // cmd4Constants.ERROR_NO_DATA_REPLY -158 "No data returned from accessory" ], // Convert our known Error Codes to strings errorString: function( index ) { let offset = - index + cmd4Constants.ERROR_STRING_MIN ; let max = cmd4Constants.ERROR_STRING_MIN - cmd4Constants.ERROR_STRING_MAX; //console.log(" index is " + index ); //console.log(" offset is " + offset ); //console.log(" max is " + max ); // i.e 0-7 if ( index == 0 ) return "Device returned SUCCESS; " + index; //if ( offset < 0 || offset > max ) if ( index > cmd4Constants.ERROR_STRING_MIN ) return "Device returned its own error; " + index; if ( index < cmd4Constants.ERROR_STRING_MAX ) return "Device returned its own error; " + index; // Should not happen because of the above checks if ( offset < 0 ) return "Invalid Error min index: " + index; // Should not happen because of the above checks if ( offset > max ) return "Invalid Error max index: " + index; return cmd4Constants.ERROR_STRINGS[ offset ]; }, // Static Messages DBUSY: "Perhaps your device is busy or unavailable?" }; module.exports = cmd4Constants; ================================================ FILE: cmd4Settings.js ================================================ "use strict"; // // This is the name of the platform that users will use to register // the plugin in the Homebridge config.json // exports.PLATFORM_NAME = "Cmd4"; // // This *MUST* match the name of your plugin as defined the package.json // exports.PLUGIN_NAME = "homebridge-cmd4"; // These must be global so that all characteristics are not // polled at the same time. Specifically a MyAir that has // multiple fans, switches and temperature sensors, all in // the same device of which a linkedAccessory is not an option. //exports.arrayOfAllStaggeredPollingCharacteristics = [ ]; exports.listOfCreatedPriorityQueues = { }; // By using our own Logger, we don't trigger others exports.cmd4Dbg = false; ================================================ FILE: commitlint.config.js ================================================ module.exports = { extends: ['@commitlint/config-conventional'] , plugins: ['commitlint-plugin-function-rules'], rules: { 'header-case': [ 0 ], 'body-max-line-length': [0], // level: disabled 'function-rules/header-case': [ 2, // level: error 'always', () => { // I do not care about case in header return [true]; } ], 'body-case': [ 0 ], 'function-rules/body-case': [ 2, // level: error 'always', () => { // I do not care about case in body return [true]; } ], 'subject-case': [ 0 ], 'function-rules/subject-case': [ 2, // level: error 'always', () => { // I do not care about case in subject return [true]; } ] } }; ================================================ FILE: docs/AdvancedTroubleShooting.md ================================================ # Homebridges-cmd4 - Advanced Trouble Shooting. ## Table of Contents * [**About Advanced Trouble Shooting**](#about-advanced-trouble-shooting) * [**The #1 Thing to Remember**](#the-1-thing-to-remember) * [**The Parameters sent by Cmd4**](#the-parameters-sent-by-cmd4) * [**Troubleshooting your own scripts**](#troubleshooting-your-own-scripts) * [***Create a middleWare shell script***](#create-a-middleware-shell-script) * [**Debug mode is your best friend**](#debug-mode-is-your-best-friend) * [**Debugging Fakegato history**](#debugging-fakegato-history) * [**Missing icons**](#missing-icons) * [**Child process error message**](#child-process-error-message) * [**License**](#license) ## About Advanced Trouble Shooting     Unlike Basic Trouble Shooting, this guide is more for those who are having problems with their own scripts and what problems can arise when integrating them with Cmd4. ## The #1 Thing to Remember     Cmd4 runs your script in the background *WITHOUT ANY ENVIRONMENT* defined. Any variables, alias, special paths are not seen by your script so even if you run the script from the command line and it works, it may not from within Cmd4. Create a bash session without any environment set up like Cmd4 does with the command:
```bash *SHELL*> env -i bash --noprofile --norc ```
    From within this environment test your script like:
```bash *SHELL*> node .homebridge/YourScriptHere.js Get My_Fan On ``` ### The Parameters Sent by Cmd4     The second most important thing to remember is what Cmd4 sends for Get/Set requests. Your script must meet these requirements. These are defined as:
``` Get < Accessory Name > < Characteristic > Set < Accessory Name > < Characteristic > < Value > ``` ## Troubleshooting your own scripts ### Execute your script from the command line interface for *Get*     Remembering that Cmd4 executes your script in a No environment setting. First execute your scripts from the CLI.
```bash *SHELL*> env -i bash --noprofile --norc *SHELL*> Get < Accessory Name > < Characteristic > ```
The script must output a one word answer or multiple quoted words.
Note: Your script must also exit with a 0 return code.
### Execute your script from the command line interface for *Set*    Your script must respond to the Set command.
```bash *SHELL*> env -i bash --noprofile --norc *SHELL*> Set < Accessory Name > < Characteristic > < value > ```
Note: Your script must also exit with a 0 return code. ### Debug mode is your best friend     As with Basic Troubleshooting, if your script passes at the CLI, run homebridge in debug mode:
    New in Cmd4 v4.0.0 is how to enable Debug mode. The logs are 100% the same, except that now that Cmd4 has its own logging system ( Copied from Homebridge for compatability ); Enabling Debug logs will not enable Debug logs in other plugins.
There are two ways to enable Cmd4 Debug logs. #### Method 1. Modifying the Cmd4 Platform section     The Cmd4 Platform section can contain the enable Debug directive. ```json { "platform": "Cmd4", "name": "Cmd4", "debug": true } ``` #### Method 2. Add DEBUG environental variable ```bash *SHELL*> DEBUG=Cmd4 ``` Note: For Homebridge-config-ui-x, you only need to write Cmd4 in the Environmental variable section. ### Create a middleWare shell script     To see when and what both Cmd4 is sending/receiving as well as what your script is sending and receiving, create a middleWare.sh script that is called from the config.json and then calls your script. A script similiar to:
``` #!/bin/bash echo $( date ) >> /tmp/Cmd4.log echo $* >> /tmp/Cmd4.log node .homebridge/Cmd4Scripts/State.js $* 2>&1 | tee -a /tmp/Cmd4.log ``` Running a "tail -f /tmp/Cmd4.log" in a seperate terminal window will show you everything that is going on between the processes.
## Debugging Fakegato history See [fakegato-history](https://github.com/simont77/fakegato-history)     if you have added fakegato history, but no history is showing, there are things you can check. ### Step 1. Check that the characteristic is polled.     Only polled characteristics are recorded. For history to be collected you will have to enable polling and interval for the accessory, and according to the fakegato-history documents it should be less than 10 minutes (600 seconds). The new polling config section allows for each characteristic to be polled at their individual times. Check your config.json for this setup. An example of polling is: ```json "polling": [{"characteristic": "currentHeatingCoolingState", "interval": 540, "timeout": 4000}, {"characteristic": "currentTemperature", "interval": 60, "timeout": 4000} ], ``` ### Step 2. Check the fakegato logs.     The storagePath stores the history records. If there are no logs, run Cmd4 in Debug mode to see if the records are being created. ### Step 3. Check your fakegato configuration.     In the following configuration, the value polled for the characteristic "currentTemperature" will be used for the designation "currentTemp". Similarly the value polled for the characteristic "targetTemperature" will be used for the designation "setTemp". This follows the fakegato-history spec and this model is used for the other fakegato-history records. ```json "fakegato":{"eve":"thermo", "currentTemp": "currentTemperature", "setTemp": "targetTemperature", "valvePosition": "0", "storage": "fs", "storagePath": ".homebridge/FakegatoStorage", "folder": "folderName", "keyPath": "/place/to/store/my/keys/" } ``` Note: The value "0" should be used for any characteristics value which is not possible to retrieve. ## Missing icons    IOS 14 added a new category characteristic to give a hint to any GUI of which icon to use. The big impact was found in missing icons for Televisions. For Televisions you must add:
```json "category": "TELEVISION" ``` This category only takes effect for Platform Accessories. ## Child process error message     If you happen to see this error message:
``` Error: Command failed: /homebridge/Server.sh Get 'Server' 'On' at ChildProcess.exithandler (child_process.js:297:12) at ChildProcess.emit (events.js:193:13) at maybeClose (internal/child_process.js:1001:16) at Process.ChildProcess._handle.onexit (internal/child_process.js:266:5) killed: true code: null signal: SIGTERM, cmd: "/homebridge/Server.sh Get Server On" ``` The command may not exist, but also the timeout value in your config.json for that accessory may be too low. ## License See [LICENSE](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/LICENSE) [homebridge]:https://github.com/nfarina/homebridge [ztalbot2000]:https://github.com/ztalbot2000 ================================================ FILE: docs/Developers.md ================================================ # Homebridges-cmd4 - Cmd4 Developers Guide. ## Table of Contents * [**About CMD4 Developers Guide**](#about-cmd4-developers-guide) * [**Where to Begin**](#where-to-begin) * [**Homebridge API**](#homebridge-api) * [**Platform Accessories**](#platform-accessories) * [**Television Accessories**](#television-accessories) * [**Cmd4 Directives**](#cmd4-directives) * [**Cmd4 Devices and Characteristics**](#cmd4-devices-and-characteristics) * [**Priority Queued Polling**](#priority-queued-polling) * [**Developer Notes**](#developer-notes) * [**Adding in Fakegato history**](#adding-in-fakegato-history) * [**Unit Testing**](#unit-testing) * [**License**](#license) ## About CMD4 Developers Guide     This document will help you understand what is needed to integrate your own scripts into Cmd4. ## Where to begin     Cmd4 comes with a fully populated and documented [**config.json**](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/Extras/config.json) file, this Developers Guide, an [Advanced](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/docs/AdvancedTroubleShooting.md) troubleshoting guide for you the script writer and finally some auto generated device and characteristic description documentation [https://ztalbot2000.github.io/homebridge-cmd4](https://ztalbot2000.github.io/homebridge-cmd4/#).
    Next you should look at scripts that might already exist. Within the Cmd4 directory structure there is a path of "Extras/Cmd4Scripts/ExampleScripts" that may already exist for you as a starting point.
* [**basic_ping.sh**](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/Extras/Cmd4Scripts/Examples/basic_ping.sh) * [**advanced_ping.sh**](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/Extras/Cmd4Scripts/Examples/advanced_ping.sh) * [**wakeonlan.sh**](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/Extras/Cmd4Scripts/Examples/wakeonlan.sh) * [**homebridge-cmd4-AdvantageAir**](https://github.com/mitch7391/homebridge-cmd4-AdvantageAir/blob/master/AdvAir.sh) * [**cmd4-HisenseTV**](https://github.com/mitch7391/cmd4-HisenseTV/blob/main/HisenseTV.sh) ## Homebridge API     Cmd4 is not possible without Homebridge. As of Cmd4 Version >= 8, Cmd4 only follows the Platform template definition as dictated by Homebridge. See: [deprecated templates](https://developers.homebridge.io/#/). This was everyone anyway and makes homebridge-ui easier to implement for Cmd4..
## Platform Accessories     An example Cmd4 Platform accessory for a Homebridge Television as described by: [Homebridge Television](https://developers.homebridge.io/#/service/Television) is: ```json { "bridge": { "name": "MAC Test Homebridge", "username": "CC:22:3D:E3:CE:30", "port": 51826, "pin": "555-55-555" }, "platforms" : [ { "platform": "Cmd4", "debug": false, "outputConstants": false, "accessories" : [ { "type": "Television", "category": "TELEVISION", "publishExternally": true, "name": "Example TV", "active": "ACTIVE", "activeIdentifier": 1, "configuredName": "Example TV", "sleepDiscoveryMode": "ALWAYS_DISCOVERABLE", "accessories": [ { "type": "TelevisionSpeaker", "displayName": "My_TelevisionSpeaker", "name": "My_TelevisionSpeaker", "active": "ENABLED", "volumeSelector": 10, "volumeControlType": "ABSOLUTE", "state_cmd": "node .homebridge/YourScriptHere.js", "polling": [ {"characteristic": "active", "interval": 50, "timeout": 5000}, {"characteristic": "volumeSelector", "interval": 50, "timeout": 5000} ] } ], "LinkedTypes": [ {"type": "InputSource", "displayName": "HDMI1", "configuredName": "HDMI 1", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "identifier": 1, "targetVisibilityState": "SHOWN", "name": "HDMI 1" }, {"type": "InputSource", "displayName": "HDMI 2", "configuredName": "HDMI 2", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "identifier": 2, "targetVisibilityState": "SHOWN", "name": "HDMI 2" }, {"type": "InputSource", "displayName": "Netflix", "configuredName": "Netflix", "currentVisibilityState": "SHOWN", "inputSourceType": "HDMI", "isConfigured": "CONFIGURED", "identifier": 3, "targetVisibilityState": "SHOWN", "name": "Netflix" } ], "displayOrder": 0, "currentMediaState": "STOP", "targetMediaState": "STOP", "pictureMode": "STANDARD", "remoteKey": "SELECT", "polling": [ {"characteristic": "active", "interval": 50, "timeout": 5000}, {"characteristic": "activeIdentifier", "interval": 50, "timeout": 5000}, {"characteristic": "currentMediaState", "interval": 540, "timeout": 5000} ], "stateChangeResponseTime": 3, "state_cmd": "node .homebridge/YourScriptHere.js" } ] } ] } ``` ### Television Accessories    There are a few special Cmd4 designations for Televisions, implemented since Cmd4 3.0
  • The first is "category"
    ```json "category": "TELEVISION" ``` This is the hint to homekit of which icon to use and for Televisions, a TV icon will not show without it.

  • The second new designation is "publishExternally"
    ```json "publishExternally": true (Default is false) ``` As per the Homebridge API, this allows the Platform Accessory to be published separately from the bridge and is a requirement for multiple TV's.
See the [Cmd4 Developers Guide](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/docs/Developers.md) for further information. ## Cmd4 Directives     Homebridge-Cmd4 has many directives, the most important being the "state_cmd". The provided config.min.json file shows many of the directives in action. A complete list of all Cmd4 directives can be found in the auto generated Cmd4 documentation found at :[https://ztalbot2000.github.io/homebridge-cmd4](https://ztalbot2000.github.io/homebridge-cmd4). ## Cmd4 Devices and Characteristics     Homebridge-Cmd4 supports 62 Devices and over 200 Characteristics which are described in the previously mentioned config.min.json file and on the auto generated github pages at: [https://ztalbot2000.github.io/homebridge-cmd4](https://ztalbot2000.github.io/homebridge-cmd4). ## Priority Queued Polling     Typically polling is pretty much a free for all. While Cmd4 tries to eleviate this with staggered polling, Cmd4 supports two kinds of Priority Queued Polling; that being "Sequential" and "WoRm" ( Write Once Read Many). If configured correctly, only one Set characteristic value can be sent or either one or multiple Gets from a device at a time. The priority given to requests from IOS first and background polling second.

Priority Queued Polling is only available when a queue is defined. The default "QueueType" being "WoRm". To configure Priority Queued Polling every characteristic to the device must be configured to be in the same queue.
The retries defaults to zero for "Get" commands from homebridge. You can alter this value as you like.
For Example of the default WoRm is: ```json "queueTypes": [ { "queue": "A", "queueType": "WoRm", "retries": 0 } ], "accessories": [ { "interval": 5, "queue: "A", "polling": [ { "characteristic": "currentTemperature" }, { "characteristic": "targetTemperature", "interval": 15 } ] } ``` The interval of the characteristic is defined through the heirarch of the Platform/Accessory and then the Characteristic, as always.
Example 2: ```json "platforms": [ { "platform": "Cmd4", "interval": "10", "queueTypes: [ { "queue": "A" : "queueType": "WoRm" } { "queue": "B" : "queueType": "Sequential" } { "queue": "C" } // Defaults to "WoRm" ], "accessories": [ { "name": "My_Thermostat", "type": "Thermostatwitch", "interval": 15, "queue": "A", "polling": [ { "characteristic": "currentTemperature" }, { "characteristic": "targetTemperature" }, { "characteristic": "currentHeatingCoolingState", "interval": 25 } ], ... }, { "name": "My_Switch", "type": "Switch", "queue": "B", "polling": [ { "characteristic": "On" } ], ... }, { "name": "My_Door", "type": "Door", "queue": "C", "polling": true, ... }, { "name": "Switch2", "type": "Switch", "queue": "D", // Defaults to WoRm "polling": [ { "characteristic": "on" } ], ... } ```
## Priority Queue Polling Across Multiple accessories ( Same Device )     In the case of a device like the My Air Smart Thermostat which has multiple accessories of Switches, Sensors and a Thermostat; Cmd4 Priority Queued Polling is essential. This device is actually why this feature was created. There was previously no possible configuration of Cmd4 staggered polling that would not result in the device locking up as it was hammered with requests at the same time. An example of configuring Cmd4 for this device is: ```json "platforms": [ { "platform": "Cmd4", "debug": false, "outputConstants": false, "timeout": 4000, "stateChangeResponseTime": 3, "queueTypes:[ { "queue": "A", "queueType": "WoRm" } ], "accessories": [ { "type": "TemperatureSensor", "subType": "tempSensor1", "displayName": "LRoom", "name": "LRoom", "currentTemperature": 25, "statusFault": "NO_FAULT", "manufacturer": "Advantage Air Australia", "model": "e-zone", "serialNumber": "Fujitsu e-zone2", "queue": "A", "polling": [ { "characteristic": "currentTemperature" } ], "props": { "currentTemperature": { "maxValue": 100, "minValue": -100, "minStep": 0.1 } }, "state_cmd": "bash /home/pi/zones.sh", "state_cmd_suffix": "z01" }, { "type": "Switch", "displayName": "TSwitch", "name": "TSwitch", "on": "FALSE", "manufacturer": "Advantage Air Australia", "model": "e-zone", "serialNumber": "Fujitsu e-zone2", "queue": "A", "polling": [ { "characteristic": "On" } ], "state_cmd": "bash /home/pi/zones.sh", "state_cmd_suffix": "z05" }, { "type": "Fan", "on": "FALSE", "displayName": "Fan", "name": "Fan", "manufacturer": "Advantage Air Australia", "model": "e-zone", "serialNumber": "Fujitsu e-zone2", "queue": "A", "polling": [ { "characteristic": "on" } ], "stateChangeResponseTime": 1, "state_cmd": "bash /home/pi/ezone.sh" }, { "type": "Thermostat", "displayName": "Aircon", "name": "Aircon", "currentHeatingCoolingState": "OFF", "targetHeatingCoolingState": "OFF", "currentTemperature": 25, "targetTemperature": 25, "temperatureDisplayUnits": "CELSIUS", "manufacturer": "Advantage Air Australia", "model": "e-zone", "serialNumber": "Fujitsu e-zone2", "queue": "A", // All characteristics would go to queue "A" "polling": [ { "characteristic": "currentHeatingCoolingState" }, { "characteristic": "targetHeatingCoolingState" }, { "characteristic": "currentTemperature" }, { "characteristic": "targetTemperature" } ], "state_cmd": "bash /home/pi/ezone.sh" } ] } ] } ``` ## Developer Notes ### Step 1. The provided jsmin differs from others     The resulting file is still readable. Only C and C++ comments are removed. The included config.json is created via: ```bash *SHELL*> gcc jsmin.c -o jsmin *SHELL*> jsmin < config.min.json > config.json ``` ### Step 2. The parameters to the state_cmd     These are defined as:
``` Get < Accessory Name > < Characteristic > Set < Accessory Name > < Characteristic > < Value > ``` ### Step 3. Polling is supported     Even if you do not use polling, care was taken that all Set Target states are immediately followed by a Get of the Current state. This is so that after closing a garage door for instance, Homekit gets updated that the door was closed. ### Step 4. Sending constants to your script.     By placing in your config.json file the tag "outputConstants": true, instead of values, your script will receive constants instead of values (Where Applicable). Homebridge-Cmd4 will except constants or values as input. See the config.min.json file for the defined constants. ### Step 5. Changing characteristic properties.     Cmd4 will allow you to change a characteristics property range, However the HomeKit GUI will most likely ignore it. The change is for those who create their own GUI's. For Example: ```json "platforms": [ { "platform": "Cmd4", "debug": false, "outputConstants": false, "timeout": 4000, "stateChangeResponseTime": 3, "accessories": [ { "type": "Thermostat", "displayName": "MyThermostat", "name": "MyThermostat", "currentTemperature": 25, "targetTemperature": 25, "props": { "currentTemperature": { "maxValue": 32, "minValue": 16, "minStep": 1 }, "targetTemperature": { "maxValue": 32, "minValue": 16, "minStep": 1 } } } ] } ] } ``` ***Important***     Homebridge-cmd4 just outputs the value to be set. For whatever reason the lower layers of homebridge set on/off to be "true" and "false" instead of 0 & 1, which is incorrect, but changing it would break others scripts.     Homebridge-cmd4 has always recognized either 0/1 or true/false when receiving the devices value. When in doubt, check the parameters yourself. Thanks Florian for pointing out the original documented bash script was incorrect     Your now ready to go and try Fans, Switches, Garage Doors, Locks, Sensors ... ✅
## Adding in Fakegato history See [fakegato-history](https://github.com/simont77/fakegato-history)     Not all accessories are supported by Eve or fakegato-history. As more and more are, they can easily be added to Homebridge-Cmd4 if they are not already by following these step. ### Step 1. Add fakegato config information     Edit your homebridge/config.json file and add to the accessory config portion the following lines: ```json "fakegato":{"eve":"thermo", "currentTemp": "currentTemperature", "setTemp": "targetTemperature", "valvePosition": "0", "storage": "fs", "storagePath": ".homebridge/FakegatoStorage", "folder": "folderName", "keyPath": "/place/to/store/my/keys/" } ``` ### Step 2. Fakegato parameters     If you notice, the parameters follow the fakegato-history spec. The only difference is that the characteristics value will be substituted for the fakegato keys. In this example "currentTemperature" will be substituted with 50.0 if that is what was returned to Cmd4. The value "0" should be used for any characteristics value which is not possible to retrieve. ### Step 3. History requires polling     For history to be collected you will have to enable polling and interval for the accessory, and according to the fakegato-history documents it should be less than 10 minutes (600 seconds). The new polling config section allows for each characteristic to be polled at their individual times. ```json "polling": [{"characteristic": "currentHeatingCoolingState", "interval": 540, "timeout": 4000}, {"characteristic": "currentTemperature", "interval": 60, "timeout": 4000} ], ``` ## Unit Testing     Unit testing is done using the Mocha framework for Javascript and was introduced in homebridge-cmd4 version 2.1.2. There are 11708 test cases and they all run successfully. They test the homebridge-Cmd4 module to make sure that all characteristics, services and names are correct. The provided config.json is also tested for proper definitions of all the homebridge-cmd4 config parameters.     Unit testing is only possible in a development environment and can be achieved in the following manner. ### Step 1. Install homebridge-cmd4 in a local environment     This is done separate from the global environment and does not impact the global environment. ```bash *SHELL*> npm install homebridge-cmd4 ``` ### Step 2. Change to the homebridge-cmd4 directory ```bash *SHELL*> cd homebridge-cmd4 ``` ### Step 3. Install homebridge-cmd4 development dependencies ```bash *SHELL*> npm install --dev ``` ### Step 4. Run the provided test cases ```bash *SHELL*> npm test ``` Note: There is one bug where the testcases do not run correctly every second attempt. It has something to do with synchronous tests for Get/Set. Please ignore it at this time. Just run it again. ## License See [LICENSE](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/LICENSE) [homebridge]:https://github.com/nfarina/homebridge [homebridge_api]:https://developers.homebridge.io/#/ [ztalbot2000]:https://github.com/ztalbot2000 ================================================ FILE: docs/autoGenerated/CMD4_AccessoryDescriptions.html ================================================ CMD4 Devices
accessCodeControlPoint accessCodeSupportedConfiguration accessControlLevel accessoryFlags accessoryIdentifier active activeIdentifier activityInterval administratorOnlyAccess airParticulateDensity airParticulateSize airPlayEnable airQuality appMatchingIdentifier assetUpdateReadiness audioFeedback batteryLevel brightness buttonEvent ccaEnergyDetectThreshold ccaSignalDetectThreshold cameraOperatingModeIndicator carbonDioxideDetected carbonDioxideLevel carbonDioxidePeakLevel carbonMonoxideDetected carbonMonoxideLevel carbonMonoxidePeakLevel characteristicValueActiveTransitionCount characteristicValueTransitionControl chargingState closedCaptions colorTemperature configurationState configuredName contactSensorState coolingThresholdTemperature cryptoHash currentAirPurifierState currentAmbientLightLevel currentDoorState currentFanState currentHeaterCoolerState currentHeatingCoolingState currentHorizontalTiltAngle currentHumidifierDehumidifierState currentMediaState currentPosition currentRelativeHumidity currentSlatState currentTemperature currentTiltAngle currentTransport currentVerticalTiltAngle currentVisibilityState dataStreamHAPTransport dataStreamHAPTransportInterrupt diagonalFieldOfView digitalZoom displayOrder eventRetransmissionMaximum eventSnapshotsActive eventTransmissionCounters filterChangeIndication filterLifeLevel firmwareRevision firmwareUpdateReadiness firmwareUpdateStatus hardwareFinish hardwareRevision heartBeat heatingThresholdTemperature holdPosition homeKitCameraActive hue identifier identify imageMirroring imageRotation inUse inputDeviceType inputSourceType isConfigured leakDetected listPairings lockControlPoint lockCurrentState lockLastKnownAction lockManagementAutoSecurityTimeout lockPhysicalControls lockTargetState logs macRetransmissionMaximum macTransmissionCounters managedNetworkEnable manuallyDisabled manufacturer MaximumTransmitPower MetricsBufferFullState model motionDetected multiFunctionButton mute NFCAccessControlPoint NFCAccessSupportedConfiguration name networkAccessViolationControl networkClientProfileControl networkClientStatusControl nightVision nitrogenDioxideDensity obstructionDetected occupancyDetected on operatingStateResponse opticalZoom outletInUse ozoneDensity pm10Density pm2_5Density pairSetup pairVerify pairingFeatures pairingPairings passwordSetting periodicSnapshotsActive pictureMode ping positionState powerModeSelection productData programMode programmableSwitchEvent programmableSwitchOutputState receivedSignalStrengthIndication receiverSensitivity recordingAudioActive relativeHumidityDehumidifierThreshold relativeHumidityHumidifierThreshold relayControlPoint relayEnabled relayState remainingDuration remoteKey resetFilterIndication rotationDirection rotationSpeed routerStatus saturation securitySystemAlarmType securitySystemCurrentState securitySystemTargetState selectedAudioStreamConfiguration selectedCameraRecordingConfiguration selectedDiagnosticsModes selectedRTPStreamConfiguration selectedSleepConfiguration serialNumber serviceLabelIndex serviceLabelNamespace setDuration setupDataStreamTransport setupEndpoints setupTransferTransport signalToNoiseRatio siriEnable SiriEndpointSessionStatus siriEngineVersion siriInputType siriLightOnUse siriListening siriTouchToUse slatType sleepDiscoveryMode sleepInterval smokeDetected softwareRevision StagedFirmwareVersion statusActive statusFault statusJammed statusLowBattery statusTampered streamingStatus sulphurDioxideDensity supportedAssetTypes supportedAudioRecordingConfiguration supportedAudioStreamConfiguration supportedCameraRecordingConfiguration supportedCharacteristicValueTransitionConfiguration supportedDataStreamTransportConfiguration supportedDiagnosticsSnapshot supportedFirmwareUpdateConfiguration supportedMetrics supportedRTPConfiguration supportedRouterConfiguration supportedTransferTransportConfiguration supportedVideoRecordingConfiguration supportedVideoStreamConfiguration swingMode tapType targetAirPurifierState targetControlList targetControlSupportedConfiguration targetDoorState targetFanState targetHeaterCoolerState targetHeatingCoolingState targetHorizontalTiltAngle targetHumidifierDehumidifierState targetMediaState targetPosition targetRelativeHumidity targetTemperature targetTiltAngle targetVerticalTiltAngle targetVisibilityState temperatureDisplayUnits thirdPartyCameraActive threadControlPoint threadNodeCapabilities threadOpenThreadVersion threadStatus token transmitPower tunnelConnectionTimeout tunneledAccessoryAdvertising tunneledAccessoryConnected tunneledAccessoryStateNumber vocDensity valveType version videoAnalysisActive volume volumeControlType volumeSelector wanConfigurationList wanStatusList wakeConfiguration waterLevel wifiCapabilities wifiConfigurationControl wifiSatelliteStatus
Developers Guide Advanced TroubleShooting

CMD4 Portal

This auto generated document replaces much of the hard coded definitions and is the portal to everything that is Cmd4.

Enjoy

   John Talbot

AccessControl

UUID:000000DA-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: AccessControl,
            name: "MY_AccessControl",
            displayName: "MY_AccessControl",
            accessControlLevel: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AccessoryRuntimeInformation

UUID:00000239-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: AccessoryRuntimeInformation,
            name: "MY_AccessoryRuntimeInformation",
            displayName: "MY_AccessoryRuntimeInformation",
            ping: "0",
            activityInterval: < UINT32 >,
            heartBeat: < UINT32 >,
            sleepInterval: < UINT32 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AccessoryInformation

UUID:0000003E-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 TRUE
Cmd4 n/a
Model n/a
My_AccessoryInformation n/a
ABC001 n/a
100.1.1 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: AccessoryInformation,
            name: "MY_AccessoryInformation",
            displayName: "MY_AccessoryInformation",
            identify: "1",
            manufacturer: "Cmd4",
            model: "Model",
            name: "My_AccessoryInformation",
            serialNumber: "ABC001",
            firmwareRevision: "100.1.1",
            hardwareRevision: "< STRING> ",
            accessoryFlags: < UINT32 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AirPurifier

UUID:000000BB-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
2 PURIFYING_AIR
1 AUTO

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: AirPurifier,
            name: "MY_AirPurifier",
            displayName: "MY_AirPurifier",
            active: "1",
            currentAirPurifierState: "2",
            targetAirPurifierState: "1",
            lockPhysicalControls: < UINT8 >,
            name: "< STRING> ",
            swingMode: < UINT8 >,
            Unknown format float,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AirQualitySensor

UUID:0000008D-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
2 GOOD

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: AirQualitySensor,
            name: "MY_AirQualitySensor",
            displayName: "MY_AirQualitySensor",
            airQuality: "2",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusTampered: < UINT8 >,
            statusLowBattery: < UINT8 >,
            name: "< STRING> ",
            Unknown format float,
            Unknown format float,
            Unknown format float,
            Unknown format float,
            Unknown format float,
            Unknown format float,
            Unknown format float,
            Unknown format float,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

BatteryService

UUID:00000096-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
50 n/a
0 NOT_CHARGING
0 BATTERY_LEVEL_NORMAL

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: BatteryService,
            name: "MY_BatteryService",
            displayName: "MY_BatteryService",
            batteryLevel: "50",
            chargingState: "0",
            statusLowBattery: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

CameraEventRecordingManagement

UUID:00000204-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
0 n/a
0 n/a
0 n/a
0 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: CameraEventRecordingManagement,
            name: "MY_CameraEventRecordingManagement",
            displayName: "MY_CameraEventRecordingManagement",
            active: "1",
            supportedCameraRecordingConfiguration: "0",
            supportedVideoRecordingConfiguration: "0",
            supportedAudioRecordingConfiguration: "0",
            selectedCameraRecordingConfiguration: "0",
            recordingAudioActive: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

CameraRTPStreamManagement

UUID:00000110-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a
0 n/a
0 n/a
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: CameraRTPStreamManagement,
            name: "MY_CameraRTPStreamManagement",
            displayName: "MY_CameraRTPStreamManagement",
            supportedVideoStreamConfiguration: "0",
            supportedAudioStreamConfiguration: "0",
            supportedRTPConfiguration: "0",
            selectedRTPStreamConfiguration: "0",
            streamingStatus: "0",
            setupEndpoints: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

CameraOperatingMode

UUID:0000021A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 DISABLE
0 OFF

Optional Characteristics

Example config.json entry

         {
            type: CameraOperatingMode,
            name: "MY_CameraOperatingMode",
            displayName: "MY_CameraOperatingMode",
            eventSnapshotsActive: "0",
            homeKitCameraActive: "0",
            cameraOperatingModeIndicator: < BOOLEAN >,
            manuallyDisabled: < BOOLEAN >,
            nightVision: < BOOLEAN >,
            periodicSnapshotsActive: < UINT8 >,
            thirdPartyCameraActive: < UINT8 >,
            Unknown format float,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

CarbonDioxideSensor

UUID:00000097-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 CO2_LEVELS_NORMAL

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: CarbonDioxideSensor,
            name: "MY_CarbonDioxideSensor",
            displayName: "MY_CarbonDioxideSensor",
            carbonDioxideDetected: "0",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusLowBattery: < UINT8 >,
            statusTampered: < UINT8 >,
            Unknown format float,
            Unknown format float,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

CarbonMonoxideSensor

UUID:0000007F-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 CO_LEVELS_NORMAL

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: CarbonMonoxideSensor,
            name: "MY_CarbonMonoxideSensor",
            displayName: "MY_CarbonMonoxideSensor",
            carbonMonoxideDetected: "0",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusLowBattery: < UINT8 >,
            statusTampered: < UINT8 >,
            Unknown format float,
            Unknown format float,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

ContactSensor

UUID:00000080-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 CONTACT_NOT_DETECTED

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: ContactSensor,
            name: "MY_ContactSensor",
            displayName: "MY_ContactSensor",
            contactSensorState: "1",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusTampered: < UINT8 >,
            statusLowBattery: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Diagnostics

UUID:00000237-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: Diagnostics,
            name: "MY_Diagnostics",
            displayName: "MY_Diagnostics",
            supportedDiagnosticsSnapshot: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Door

UUID:00000081-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
2 STOPPED
0 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Door,
            name: "MY_Door",
            displayName: "MY_Door",
            currentPosition: "0",
            positionState: "2",
            targetPosition: "0",
            holdPosition: < BOOLEAN >,
            obstructionDetected: < BOOLEAN >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

DoorBell

UUID:00000121-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 SINGLE_PRESS

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: DoorBell,
            name: "MY_DoorBell",
            displayName: "MY_DoorBell",
            programmableSwitchEvent: "0",
            brightness: < INT >,
            mute: < BOOLEAN >,
            name: "< STRING> ",
            volume: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Fan

UUID:00000040-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Fan,
            name: "MY_Fan",
            displayName: "MY_Fan",
            on: "0",
            rotationDirection: < INT >,
            Unknown format float,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Fanv1

UUID:00000040-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Fanv1,
            name: "MY_Fanv1",
            displayName: "MY_Fanv1",
            on: "0",
            rotationDirection: < INT >,
            Unknown format float,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Fanv2

UUID:000000B7-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Fanv2,
            name: "MY_Fanv2",
            displayName: "MY_Fanv2",
            active: "1",
            currentFanState: < UINT8 >,
            targetFanState: < UINT8 >,
            lockPhysicalControls: < UINT8 >,
            name: "< STRING> ",
            rotationDirection: < INT >,
            Unknown format float,
            swingMode: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Faucet

UUID:000000D7-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Faucet,
            name: "MY_Faucet",
            displayName: "MY_Faucet",
            active: "1",
            name: "< STRING> ",
            statusFault: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

FilterMaintenance

UUID:000000BA-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FILTER_OK

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: FilterMaintenance,
            name: "MY_FilterMaintenance",
            displayName: "MY_FilterMaintenance",
            filterChangeIndication: "0",
            Unknown format float,
            resetFilterIndication: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

GarageDoorOpener

UUID:00000041-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 OPEN
0 OPEN
1 TRUE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: GarageDoorOpener,
            name: "MY_GarageDoorOpener",
            displayName: "MY_GarageDoorOpener",
            currentDoorState: "0",
            targetDoorState: "0",
            obstructionDetected: "1",
            lockCurrentState: < UINT8 >,
            lockTargetState: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

HeaterCooler

UUID:000000BC-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
0 INACTIVE
1 HEAT
22.2 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: HeaterCooler,
            name: "MY_HeaterCooler",
            displayName: "MY_HeaterCooler",
            active: "1",
            currentHeaterCoolerState: "0",
            targetHeaterCoolerState: "1",
            currentTemperature: "22.2",
            lockPhysicalControls: < UINT8 >,
            name: "< STRING> ",
            Unknown format float,
            swingMode: < UINT8 >,
            Unknown format float,
            Unknown format float,
            temperatureDisplayUnits: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

HumidifierDehumidifier

UUID:000000BD-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
60 n/a
1 IDLE
2 DEHUMIDIFIER
1 ACTIVE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: HumidifierDehumidifier,
            name: "MY_HumidifierDehumidifier",
            displayName: "MY_HumidifierDehumidifier",
            currentRelativeHumidity: "60",
            currentHumidifierDehumidifierState: "1",
            targetHumidifierDehumidifierState: "2",
            active: "1",
            lockPhysicalControls: < UINT8 >,
            name: "< STRING> ",
            Unknown format float,
            Unknown format float,
            Unknown format float,
            swingMode: < UINT8 >,
            Unknown format float,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

HumiditySensor

UUID:00000082-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
60 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: HumiditySensor,
            name: "MY_HumiditySensor",
            displayName: "MY_HumiditySensor",
            currentRelativeHumidity: "60",
            name: "< STRING> ",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusTampered: < UINT8 >,
            statusLowBattery: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

InputSource

UUID:000000D9-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
My_InputSource n/a
1 HOME_SCREEN
1 CONFIGURED
0 SHOWN

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: InputSource,
            name: "MY_InputSource",
            displayName: "MY_InputSource",
            configuredName: "My_InputSource",
            inputSourceType: "1",
            isConfigured: "1",
            currentVisibilityState: "0",
            identifier: < UINT32 >,
            inputDeviceType: < UINT8 >,
            targetVisibilityState: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

IrrigationSystem

UUID:000000CF-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
0 NO_PROGRAM_SCHEDULED
1 IN_USE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: IrrigationSystem,
            name: "MY_IrrigationSystem",
            displayName: "MY_IrrigationSystem",
            active: "1",
            programMode: "0",
            inUse: "1",
            name: "< STRING> ",
            remainingDuration: < UINT32 >,
            statusFault: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

LeakSensor

UUID:00000083-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 LEAK_NOT_DETECTED

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: LeakSensor,
            name: "MY_LeakSensor",
            displayName: "MY_LeakSensor",
            leakDetected: "0",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusTampered: < UINT8 >,
            statusLowBattery: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

LightSensor

UUID:00000084-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: LightSensor,
            name: "MY_LightSensor",
            displayName: "MY_LightSensor",
            currentAmbientLightLevel: "1",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusTampered: < UINT8 >,
            statusLowBattery: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Lightbulb

UUID:00000043-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Lightbulb,
            name: "MY_Lightbulb",
            displayName: "MY_Lightbulb",
            on: "0",
            brightness: < INT >,
            Unknown format float,
            Unknown format float,
            name: "< STRING> ",
            colorTemperature: < INT >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

LockManagement

UUID:00000044-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0.0.0 n/a

Optional Characteristics

Example config.json entry

         {
            type: LockManagement,
            name: "MY_LockManagement",
            displayName: "MY_LockManagement",
            lockControlPoint: "0",
            version: "0.0.0",
            administratorOnlyAccess: < BOOLEAN >,
            audioFeedback: < BOOLEAN >,
            currentDoorState: < UINT8 >,
            lockManagementAutoSecurityTimeout: < UINT32 >,
            lockLastKnownAction: < UINT8 >,
            motionDetected: < BOOLEAN >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

LockMechanism

UUID:00000045-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 UNSECURED
0 UNSECURED

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: LockMechanism,
            name: "MY_LockMechanism",
            displayName: "MY_LockMechanism",
            lockCurrentState: "0",
            lockTargetState: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Microphone

UUID:00000112-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Microphone,
            name: "MY_Microphone",
            displayName: "MY_Microphone",
            mute: "0",
            volume: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

MotionSensor

UUID:00000085-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 TRUE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: MotionSensor,
            name: "MY_MotionSensor",
            displayName: "MY_MotionSensor",
            motionDetected: "1",
            name: "< STRING> ",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusLowBattery: < UINT8 >,
            statusTampered: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

OccupancySensor

UUID:00000086-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 OCCUPANCY_NOT_DETECTED

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: OccupancySensor,
            name: "MY_OccupancySensor",
            displayName: "MY_OccupancySensor",
            occupancyDetected: "0",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusTampered: < UINT8 >,
            statusLowBattery: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Outlet

UUID:00000047-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Outlet,
            name: "MY_Outlet",
            displayName: "MY_Outlet",
            on: "0",
            outletInUse: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Pairing

UUID:00000055-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 n/a
0 n/a
0 n/a
0 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Pairing,
            name: "MY_Pairing",
            displayName: "MY_Pairing",
            listPairings: "1",
            pairSetup: "0",
            pairVerify: "0",
            pairingFeatures: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

PowerManagement

UUID:00000221-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: PowerManagement,
            name: "MY_PowerManagement",
            displayName: "MY_PowerManagement",
            wakeConfiguration: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

ProtocolInformation

UUID:000000A2-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1.2.3 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: ProtocolInformation,
            name: "MY_ProtocolInformation",
            displayName: "MY_ProtocolInformation",
            version: "1.2.3",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Relay

UUID:0000005A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 TRUE
1 n/a
0 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Relay,
            name: "MY_Relay",
            displayName: "MY_Relay",
            relayEnabled: "1",
            relayState: "1",
            relayControlPoint: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

SecuritySystem

UUID:0000007E-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
3 DISARMED
3 DISARM

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: SecuritySystem,
            name: "MY_SecuritySystem",
            displayName: "MY_SecuritySystem",
            securitySystemCurrentState: "3",
            securitySystemTargetState: "3",
            name: "< STRING> ",
            securitySystemAlarmType: < UINT8 >,
            statusFault: < UINT8 >,
            statusTampered: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

ServiceLabel

UUID:000000CC-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 DOTS

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: ServiceLabel,
            name: "MY_ServiceLabel",
            displayName: "MY_ServiceLabel",
            serviceLabelNamespace: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Siri

UUID:00000133-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 PUSH_BUTTON_TRIGGERED_APPLE_TV

Optional Characteristics

Example config.json entry

         {
            type: Siri,
            name: "MY_Siri",
            displayName: "MY_Siri",
            siriInputType: "0",
            multiFunctionButton: < UINT8 >,
            siriEnable: < UINT8 >,
            siriEngineVersion: "< STRING> ",
            siriLightOnUse: < UINT8 >,
            siriListening: < UINT8 >,
            siriTouchToUse: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Slats

UUID:000000B9-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FIXED
0 HORIZONTAL

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Slats,
            name: "MY_Slats",
            displayName: "MY_Slats",
            currentSlatState: "0",
            slatType: "0",
            name: "< STRING> ",
            swingMode: < UINT8 >,
            currentTiltAngle: < INT >,
            targetTiltAngle: < INT >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

SmartSpeaker

UUID:00000228-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
2 STOP
2 STOP

Optional Characteristics

Example config.json entry

         {
            type: SmartSpeaker,
            name: "MY_SmartSpeaker",
            displayName: "MY_SmartSpeaker",
            currentMediaState: "2",
            targetMediaState: "2",
            airPlayEnable: < UINT8 >,
            configuredName: "< STRING> ",
            mute: < BOOLEAN >,
            name: "< STRING> ",
            volume: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

SmokeSensor

UUID:00000087-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 SMOKE_NOT_DETECTED

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: SmokeSensor,
            name: "MY_SmokeSensor",
            displayName: "MY_SmokeSensor",
            smokeDetected: "0",
            name: "< STRING> ",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusLowBattery: < UINT8 >,
            statusTampered: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Speaker

UUID:00000113-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Speaker,
            name: "MY_Speaker",
            displayName: "MY_Speaker",
            mute: "0",
            active: < UINT8 >,
            volume: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

StatefulProgrammableSwitch

UUID:00000088-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 SINGLE_PRESS
0 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: StatefulProgrammableSwitch,
            name: "MY_StatefulProgrammableSwitch",
            displayName: "MY_StatefulProgrammableSwitch",
            programmableSwitchEvent: "0",
            programmableSwitchOutputState: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

StatelessProgrammableSwitch

UUID:00000089-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 SINGLE_PRESS

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: StatelessProgrammableSwitch,
            name: "MY_StatelessProgrammableSwitch",
            displayName: "MY_StatelessProgrammableSwitch",
            programmableSwitchEvent: "0",
            name: "< STRING> ",
            serviceLabelIndex: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Switch

UUID:00000049-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Switch,
            name: "MY_Switch",
            displayName: "MY_Switch",
            on: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

TargetControl

UUID:00000125-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
7 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: TargetControl,
            name: "MY_TargetControl",
            displayName: "MY_TargetControl",
            active: "1",
            activeIdentifier: "7",
            buttonEvent: "0",
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

TargetControlManagement

UUID:00000122-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: TargetControlManagement,
            name: "MY_TargetControlManagement",
            displayName: "MY_TargetControlManagement",
            targetControlSupportedConfiguration: "0",
            targetControlList: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Television

UUID:000000D8-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
123 n/a
My_Tv n/a
8 SELECT
1 ALWAYS_DISCOVERABLE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Television,
            name: "MY_Television",
            displayName: "MY_Television",
            active: "1",
            activeIdentifier: "123",
            configuredName: "My_Tv",
            remoteKey: "8",
            sleepDiscoveryMode: "1",
            brightness: < INT >,
            closedCaptions: < UINT8 >,
            currentMediaState: < UINT8 >,
            targetMediaState: < UINT8 >,
            name: "< STRING> ",
            pictureMode: < UINT8 >,
            powerModeSelection: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

TelevisionSpeaker

UUID:00000113-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: TelevisionSpeaker,
            name: "MY_TelevisionSpeaker",
            displayName: "MY_TelevisionSpeaker",
            mute: "0",
            active: < UINT8 >,
            volume: < UINT8 >,
            volumeControlType: < UINT8 >,
            volumeSelector: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

TemperatureSensor

UUID:0000008A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
50 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: TemperatureSensor,
            name: "MY_TemperatureSensor",
            displayName: "MY_TemperatureSensor",
            currentTemperature: "50",
            name: "< STRING> ",
            statusActive: < BOOLEAN >,
            statusFault: < UINT8 >,
            statusLowBattery: < UINT8 >,
            statusTampered: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Thermostat

UUID:0000004A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 OFF
0 OFF
50 n/a
50 n/a
0 CELSIUS

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Thermostat,
            name: "MY_Thermostat",
            displayName: "MY_Thermostat",
            currentHeatingCoolingState: "0",
            targetHeatingCoolingState: "0",
            currentTemperature: "50",
            targetTemperature: "50",
            temperatureDisplayUnits: "0",
            name: "< STRING> ",
            Unknown format float,
            Unknown format float,
            Unknown format float,
            Unknown format float,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

TransferTransportManagement

UUID:00000203-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: TransferTransportManagement,
            name: "MY_TransferTransportManagement",
            displayName: "MY_TransferTransportManagement",
            supportedTransferTransportConfiguration: "0",
            setupTransferTransport: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

TunneledBTLEAccessoryService

UUID:00000056-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
My_TunnelB n/a
TLB n/a
0 n/a
0 FALSE
0 FALSE
5000 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: TunneledBTLEAccessoryService,
            name: "MY_TunneledBTLEAccessoryService",
            displayName: "MY_TunneledBTLEAccessoryService",
            name: "My_TunnelB",
            accessoryIdentifier: "TLB",
            tunneledAccessoryStateNumber: "0",
            tunneledAccessoryConnected: "0",
            tunneledAccessoryAdvertising: "0",
            tunnelConnectionTimeout: "5000",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Valve

UUID:000000D0-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
1 IN_USE
0 GENERIC_VALVE

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Valve,
            name: "MY_Valve",
            displayName: "MY_Valve",
            active: "1",
            inUse: "1",
            valveType: "0",
            isConfigured: < UINT8 >,
            name: "< STRING> ",
            remainingDuration: < UINT32 >,
            serviceLabelIndex: < UINT8 >,
            setDuration: < UINT32 >,
            statusFault: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

WiFiRouter

UUID:0000020A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
My_WiFiRouter n/a
0 DISABLED
0 n/a
0 n/a
0 n/a
0 READY
0 n/a
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: WiFiRouter,
            name: "MY_WiFiRouter",
            displayName: "MY_WiFiRouter",
            configuredName: "My_WiFiRouter",
            managedNetworkEnable: "0",
            networkAccessViolationControl: "0",
            networkClientProfileControl: "0",
            networkClientStatusControl: "0",
            routerStatus: "0",
            supportedRouterConfiguration: "0",
            wanConfigurationList: "0",
            wanStatusList: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

WiFiSatellite

UUID:0000020F-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
2 NOT_CONNECTED

Optional Characteristics

Example config.json entry

         {
            type: WiFiSatellite,
            name: "MY_WiFiSatellite",
            displayName: "MY_WiFiSatellite",
            wifiSatelliteStatus: "2",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Window

UUID:0000008B-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
2 STOPPED
0 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: Window,
            name: "MY_Window",
            displayName: "MY_Window",
            currentPosition: "0",
            positionState: "2",
            targetPosition: "0",
            name: "< STRING> ",
            obstructionDetected: < BOOLEAN >,
            holdPosition: < BOOLEAN >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

WindowCovering

UUID:0000008C-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
2 STOPPED
0 n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: WindowCovering,
            name: "MY_WindowCovering",
            displayName: "MY_WindowCovering",
            currentPosition: "0",
            positionState: "2",
            targetPosition: "0",
            currentHorizontalTiltAngle: < INT >,
            targetHorizontalTiltAngle: < INT >,
            name: "< STRING> ",
            obstructionDetected: < BOOLEAN >,
            holdPosition: < BOOLEAN >,
            currentVerticalTiltAngle: < INT >,
            targetVerticalTiltAngle: < INT >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AccessoryMetrics

UUID:00000270-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
1 n/a
n/a

Optional Characteristics

Default Polling Characteristics

Default polling characteristics are characteristics that would automatically be polled if Accessory Polling is configured ( polling:true ) instead of characteristic polling where each characteristic is set individually.

Example config.json entry

         {
            type: AccessoryMetrics,
            name: "MY_AccessoryMetrics",
            displayName: "MY_AccessoryMetrics",
            active: "1",
            MetricsBufferFullState: "1",
            supportedMetrics: "",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AssetUpdate

UUID:00000267-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: AssetUpdate,
            name: "MY_AssetUpdate",
            displayName: "MY_AssetUpdate",
            assetUpdateReadiness: "0",
            supportedAssetTypes: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Assistant

UUID:0000026A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
Some Identifier n/a
Unnamed Assistant n/a

Optional Characteristics

Example config.json entry

         {
            type: Assistant,
            name: "MY_Assistant",
            displayName: "MY_Assistant",
            active: "1",
            identifier: "Some Identifier",
            name: "Unnamed Assistant",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AudioStreamManagement

UUID:00000127-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: AudioStreamManagement,
            name: "MY_AudioStreamManagement",
            displayName: "MY_AudioStreamManagement",
            supportedAudioStreamConfiguration: "0",
            selectedAudioStreamConfiguration: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

Battery

UUID:00000096-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 BATTERY_LEVEL_NORMAL

Optional Characteristics

Example config.json entry

         {
            type: Battery,
            name: "MY_Battery",
            displayName: "MY_Battery",
            statusLowBattery: "0",
            batteryLevel: < UINT8 >,
            chargingState: < UINT8 >,
            name: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

CameraRecordingManagement

UUID:00000204-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
0 n/a
0 n/a
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: CameraRecordingManagement,
            name: "MY_CameraRecordingManagement",
            displayName: "MY_CameraRecordingManagement",
            active: "1",
            supportedCameraRecordingConfiguration: "0",
            supportedVideoRecordingConfiguration: "0",
            supportedAudioRecordingConfiguration: "0",
            selectedCameraRecordingConfiguration: "0",
            recordingAudioActive: < UINT8 >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

CloudRelay

UUID:0000005A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
1 n/a
false FALSE

Optional Characteristics

Example config.json entry

         {
            type: CloudRelay,
            name: "MY_CloudRelay",
            displayName: "MY_CloudRelay",
            relayControlPoint: "0",
            relayState: "1",
            relayEnabled: "false",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

DataStreamTransportManagement

UUID:00000129-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a
Unknown version n/a

Optional Characteristics

Example config.json entry

         {
            type: DataStreamTransportManagement,
            name: "MY_DataStreamTransportManagement",
            displayName: "MY_DataStreamTransportManagement",
            setupDataStreamTransport: "0",
            supportedCharacteristicValueTransitionConfiguration: "0",
            version: "Unknown version",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

NFCAccess

UUID:00000266-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: NFCAccess,
            name: "MY_NFCAccess",
            displayName: "MY_NFCAccess",
            configurationState: "0",
            NFCAccessControlPoint: "0",
            NFCAccessSupportedConfiguration: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

SiriEndpoint

UUID:00000253-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
Unknown version n/a

Optional Characteristics

Example config.json entry

         {
            type: SiriEndpoint,
            name: "MY_SiriEndpoint",
            displayName: "MY_SiriEndpoint",
            SiriEndpointSessionStatus: "0",
            version: "Unknown version",
            activeIdentifier: < UINT32 >,
            manuallyDisabled: < BOOLEAN >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

ThreadTransport

UUID:00000701-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE
0 n/a
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: ThreadTransport,
            name: "MY_ThreadTransport",
            displayName: "MY_ThreadTransport",
            currentTransport: "0",
            threadControlPoint: "0",
            threadNodeCapabilities: "0",
            threadStatus: "0",
            ccaEnergyDetectThreshold: < INT >,
            ccaSignalDetectThreshold: < INT >,
            eventRetransmissionMaximum: < UINT8 >,
            eventTransmissionCounters: < UINT32 >,
            macRetransmissionMaximum: < UINT8 >,
            macTransmissionCounters: < DATA >,
            receiverSensitivity: < INT >,
            receivedSignalStrengthIndication: < INT >,
            signalToNoiseRatio: < INT >,
            threadOpenThreadVersion: "< STRING> ",
            transmitPower: < INT >,
            MaximumTransmitPower: < INT >,
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

AccessCode

UUID:00000260-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: AccessCode,
            name: "MY_AccessCode",
            displayName: "MY_AccessCode",
            accessCodeControlPoint: "0",
            accessCodeSupportedConfiguration: "0",
            configurationState: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

FirmwareUpdate

UUID:00000236-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 n/a
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: FirmwareUpdate,
            name: "MY_FirmwareUpdate",
            displayName: "MY_FirmwareUpdate",
            firmwareUpdateReadiness: "0",
            firmwareUpdateStatus: "0",
            StagedFirmwareVersion: "< STRING> ",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

TapManagement

UUID:0000022E-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
1 ACTIVE
0 n/a
0 n/a
n/a

Optional Characteristics

Example config.json entry

         {
            type: TapManagement,
            name: "MY_TapManagement",
            displayName: "MY_TapManagement",
            active: "1",
            cryptoHash: "0",
            tapType: "0",
            token: "",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

WiFiTransport

UUID:0000022A-0000-1000-8000-0026BB765291

Required Characteristics

Default Value Constant
0 FALSE
0 n/a

Optional Characteristics

Example config.json entry

         {
            type: WiFiTransport,
            name: "MY_WiFiTransport",
            displayName: "MY_WiFiTransport",
            currentTransport: "0",
            wifiCapabilities: "0",
            state_cmd: < "STRING" >,
            polling: < POLLING>

         }
         
See additional CMD4 Accessory Directives

accessControlLevel

Properties

Type: accessControlLevel
UUID: 000000E5-0000-1000-8000-0026BB765291
Format: uint16
Max Value: 2
Min Step: 1
Perms: Events, Paired Read, Paired Write

accessoryFlags

Properties

Type: accessoryFlags
UUID: 000000A6-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read, Events

accessoryIdentifier

Properties

Type: accessoryIdentifier
UUID: 00000057-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

active

Properties

Type: active
UUID: 000000B0-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "INACTIVE"
1 "ACTIVE"

activeIdentifier

Properties

Type: activeIdentifier
UUID: 000000E7-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read, Paired Write, Events

activityInterval

Properties

Type: activityInterval
UUID: 0000023B-0000-1000-8000-0026BB765291
Format: uint32
Min Step: 1
Perms: Paired Read, Events

administratorOnlyAccess

Properties

Type: administratorOnlyAccess
UUID: 00000001-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

airParticulateDensity

Properties

Type: airParticulateDensity
UUID: 00000064-0000-1000-8000-0026BB765291
Format: float
Max Value: 1000
Perms: Paired Read, Events

airParticulateSize

Properties

Type: airParticulateSize
UUID: 00000065-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "_2_5_M"
1 "_10_M"

airQuality

Properties

Type: airQuality
UUID: 00000095-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 5
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "UNKNOWN"
1 "EXCELLENT"
2 "GOOD"
3 "FAIR"
4 "INFERIOR"
5 "POOR"

appMatchingIdentifier

Properties

Type: appMatchingIdentifier
UUID: 000000A4-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

audioFeedback

Properties

Type: audioFeedback
UUID: 00000005-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

batteryLevel

Properties

Type: batteryLevel
UUID: 00000068-0000-1000-8000-0026BB765291
Format: uint8
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Events

brightness

Properties

Type: brightness
UUID: 00000008-0000-1000-8000-0026BB765291
Format: int
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

buttonEvent

Properties

Type: buttonEvent
UUID: 00000126-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

ccaEnergyDetectThreshold

Properties

Type: ccaEnergyDetectThreshold
UUID: 00000246-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read

ccaSignalDetectThreshold

Properties

Type: ccaSignalDetectThreshold
UUID: 00000245-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read

cameraOperatingModeIndicator

Properties

Type: cameraOperatingModeIndicator
UUID: 0000021D-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events, Timed Write

Constants

Value String
0 "DISABLE"
1 "ENABLE"

carbonDioxideDetected

Properties

Type: carbonDioxideDetected
UUID: 00000092-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "CO2_LEVELS_NORMAL"
1 "CO2_LEVELS_ABNORMAL"

carbonDioxideLevel

Properties

Type: carbonDioxideLevel
UUID: 00000093-0000-1000-8000-0026BB765291
Format: float
Max Value: 100000
Min Step: 1
Perms: Paired Read, Events

carbonDioxidePeakLevel

Properties

Type: carbonDioxidePeakLevel
UUID: 00000094-0000-1000-8000-0026BB765291
Format: float
Max Value: 100000
Min Step: 1
Perms: Paired Read, Events

carbonMonoxideDetected

Properties

Type: carbonMonoxideDetected
UUID: 00000069-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "CO_LEVELS_NORMAL"
1 "CO_LEVELS_ABNORMAL"

carbonMonoxideLevel

Properties

Type: carbonMonoxideLevel
UUID: 00000090-0000-1000-8000-0026BB765291
Format: float
Max Value: 100
Min Step: 1
Perms: Paired Read, Events

carbonMonoxidePeakLevel

Properties

Type: carbonMonoxidePeakLevel
UUID: 00000091-0000-1000-8000-0026BB765291
Format: float
Max Value: 100
Min Step: 1
Perms: Paired Read, Events

characteristicValueTransitionControl

Properties

Type: characteristicValueTransitionControl
UUID: 00000143-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Write Response

chargingState

Properties

Type: chargingState
UUID: 0000008F-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NOT_CHARGING"
1 "CHARGING"
2 "NOT_CHARGEABLE"

closedCaptions

Properties

Type: closedCaptions
UUID: 000000DD-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "DISABLED"
1 "ENABLED"

colorTemperature

Properties

Type: colorTemperature
UUID: 000000CE-0000-1000-8000-0026BB765291
Format: int
Min Value: 140
Max Value: 500
Min Step: 1
Perms: Paired Read, Paired Write, Events

configuredName

Properties

Type: configuredName
UUID: 000000E3-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read, Paired Write, Events

contactSensorState

Properties

Type: contactSensorState
UUID: 0000006A-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "CONTACT_DETECTED"
1 "CONTACT_NOT_DETECTED"

coolingThresholdTemperature

Properties

Type: coolingThresholdTemperature
UUID: 0000000D-0000-1000-8000-0026BB765291
Format: float
Unit: celsius
Min Value: 10
Max Value: 35
Min Step: 0.1
Perms: Paired Read, Paired Write, Events

currentAirPurifierState

Properties

Type: currentAirPurifierState
UUID: 000000A9-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "INACTIVE"
1 "IDLE"
2 "PURIFYING_AIR"

currentAmbientLightLevel

Properties

Type: currentAmbientLightLevel
UUID: 0000006B-0000-1000-8000-0026BB765291
Format: float
Unit: lux
Min Value: 0.0001
Max Value: 100000
Perms: Paired Read, Events

currentDoorState

Properties

Type: currentDoorState
UUID: 0000000E-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 4
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "OPEN"
1 "CLOSED"
2 "OPENING"
3 "CLOSING"
4 "STOPPED"

currentFanState

Properties

Type: currentFanState
UUID: 000000AF-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "INACTIVE"
1 "IDLE"
2 "BLOWING_AIR"

currentHeaterCoolerState

Properties

Type: currentHeaterCoolerState
UUID: 000000B1-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "INACTIVE"
1 "IDLE"
2 "HEATING"
3 "COOLING"

currentHeatingCoolingState

Properties

Type: currentHeatingCoolingState
UUID: 0000000F-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "OFF"
1 "HEAT"
2 "COOL"
3 "AUTO"

currentHorizontalTiltAngle

Properties

Type: currentHorizontalTiltAngle
UUID: 0000006C-0000-1000-8000-0026BB765291
Format: int
Unit: arcdegrees
Min Value: -90
Max Value: 90
Min Step: 1
Perms: Paired Read, Events

currentHumidifierDehumidifierState

Properties

Type: currentHumidifierDehumidifierState
UUID: 000000B3-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "INACTIVE"
1 "IDLE"
2 "HUMIDIFYING"
3 "DEHUMIDIFYING"

currentMediaState

Properties

Type: currentMediaState
UUID: 000000E0-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 5
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "PLAY"
1 "PAUSE"
2 "STOP"
4 "LOADING"
5 "INTERRUPTED"

currentPosition

Properties

Type: currentPosition
UUID: 0000006D-0000-1000-8000-0026BB765291
Format: uint8
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Events

currentRelativeHumidity

Properties

Type: currentRelativeHumidity
UUID: 00000010-0000-1000-8000-0026BB765291
Format: float
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Events

currentSlatState

Properties

Type: currentSlatState
UUID: 000000AA-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "FIXED"
1 "JAMMED"
2 "SWINGING"

currentTemperature

Properties

Type: currentTemperature
UUID: 00000011-0000-1000-8000-0026BB765291
Format: float
Unit: celsius
Min Value: -270
Max Value: 100
Min Step: 0.1
Perms: Paired Read, Events

currentTiltAngle

Properties

Type: currentTiltAngle
UUID: 000000C1-0000-1000-8000-0026BB765291
Format: int
Unit: arcdegrees
Min Value: -90
Max Value: 90
Min Step: 1
Perms: Paired Read, Events

currentTransport

Properties

Type: currentTransport
UUID: 0000022B-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read

Constants

Value String
0 "FALSE"
1 "TRUE"

currentVerticalTiltAngle

Properties

Type: currentVerticalTiltAngle
UUID: 0000006E-0000-1000-8000-0026BB765291
Format: int
Unit: arcdegrees
Min Value: -90
Max Value: 90
Min Step: 1
Perms: Paired Read, Events

currentVisibilityState

Properties

Type: currentVisibilityState
UUID: 00000135-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "SHOWN"
1 "HIDDEN"

dataStreamHAPTransport

Properties

Type: dataStreamHAPTransport
UUID: 00000138-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Write Response

dataStreamHAPTransportInterrupt

Properties

Type: dataStreamHAPTransportInterrupt
UUID: 00000139-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

diagonalFieldOfView

Properties

Type: diagonalFieldOfView
UUID: 00000224-0000-1000-8000-0026BB765291
Format: float
Unit: arcdegrees
Max Value: 360
Perms: Paired Read, Events

digitalZoom

Properties

Type: digitalZoom
UUID: 0000011D-0000-1000-8000-0026BB765291
Format: float
Min Step: 0.1
Perms: Paired Read, Paired Write, Events

displayOrder

Properties

Type: displayOrder
UUID: 00000136-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Events

eventRetransmissionMaximum

Properties

Type: eventRetransmissionMaximum
UUID: 0000023D-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read

eventSnapshotsActive

Properties

Type: eventSnapshotsActive
UUID: 00000223-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "DISABLE"
1 "ENABLE"

eventTransmissionCounters

Properties

Type: eventTransmissionCounters
UUID: 0000023E-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read

filterChangeIndication

Properties

Type: filterChangeIndication
UUID: 000000AC-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "FILTER_OK"
1 "CHANGE_FILTER"

filterLifeLevel

Properties

Type: filterLifeLevel
UUID: 000000AB-0000-1000-8000-0026BB765291
Format: float
Max Value: 100
Min Step: 1
Perms: Paired Read, Events

firmwareRevision

Properties

Type: firmwareRevision
UUID: 00000052-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

hardwareRevision

Properties

Type: hardwareRevision
UUID: 00000053-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

heartBeat

Properties

Type: heartBeat
UUID: 0000024A-0000-1000-8000-0026BB765291
Format: uint32
Perms: Events, Paired Read

heatingThresholdTemperature

Properties

Type: heatingThresholdTemperature
UUID: 00000012-0000-1000-8000-0026BB765291
Format: float
Unit: celsius
Max Value: 25
Min Step: 0.1
Perms: Paired Read, Paired Write, Events

holdPosition

Properties

Type: holdPosition
UUID: 0000006F-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Write

Constants

Value String
0 "FALSE"
1 "TRUE"

homeKitCameraActive

Properties

Type: homeKitCameraActive
UUID: 0000021B-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "OFF"
1 "ON"

hue

Properties

Type: hue
UUID: 00000013-0000-1000-8000-0026BB765291
Format: float
Unit: arcdegrees
Max Value: 360
Min Step: 1
Perms: Paired Read, Paired Write, Events

identifier

Properties

Type: identifier
UUID: 000000E6-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read

identify

Properties

Type: identify
UUID: 00000014-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Write

Constants

Value String
0 "FALSE"
1 "TRUE"

imageMirroring

Properties

Type: imageMirroring
UUID: 0000011F-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

imageRotation

Properties

Type: imageRotation
UUID: 0000011E-0000-1000-8000-0026BB765291
Format: int
Unit: arcdegrees
Max Value: 360
Min Step: 1
Perms: Paired Read, Paired Write, Events

inUse

Properties

Type: inUse
UUID: 000000D2-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NOT_IN_USE"
1 "IN_USE"

inputDeviceType

Properties

Type: inputDeviceType
UUID: 000000DC-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 6
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "OTHER"
1 "TV"
2 "RECORDING"
3 "TUNER"
4 "PLAYBACK"
5 "AUDIO_SYSTEM"
undefined "UNKNOWN_6"

inputSourceType

Properties

Type: inputSourceType
UUID: 000000DB-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 10
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "OTHER"
1 "HOME_SCREEN"
2 "TUNER"
3 "HDMI"
4 "COMPOSITE_VIDEO"
5 "S_VIDEO"
6 "COMPONENT_VIDEO"
7 "DVI"
8 "AIRPLAY"
9 "USB"
10 "APPLICATION"

isConfigured

Properties

Type: isConfigured
UUID: 000000D6-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "NOT_CONFIGURED"
1 "CONFIGURED"

leakDetected

Properties

Type: leakDetected
UUID: 00000070-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "LEAK_NOT_DETECTED"
1 "LEAK_DETECTED"

listPairings

Properties

Type: listPairings
UUID: 00000050-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

lockControlPoint

Properties

Type: lockControlPoint
UUID: 00000019-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Write

lockCurrentState

Properties

Type: lockCurrentState
UUID: 0000001D-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "UNSECURED"
1 "SECURED"
2 "JAMMED"
3 "UNKNOWN"

lockLastKnownAction

Properties

Type: lockLastKnownAction
UUID: 0000001C-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 10
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "SECURED_PHYSICALLY_INTERIOR"
1 "UNSECURED_PHYSICALLY_INTERIOR"
2 "SECURED_PHYSICALLY_EXTERIOR"
3 "UNSECURED_PHYSICALLY_EXTERIOR"
4 "SECURED_BY_KEYPAD"
5 "UNSECURED_BY_KEYPAD"
6 "SECURED_REMOTELY"
7 "UNSECURED_REMOTELY"
8 "SECURED_BY_AUTO_SECURE_TIMEOUT"
9 "SECURED_PHYSICALLY"
10 "UNSECURED_PHYSICALLY"

lockManagementAutoSecurityTimeout

Properties

Type: lockManagementAutoSecurityTimeout
UUID: 0000001A-0000-1000-8000-0026BB765291
Format: uint32
Unit: seconds
Perms: Paired Read, Paired Write, Events

lockPhysicalControls

Properties

Type: lockPhysicalControls
UUID: 000000A7-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "CONTROL_LOCK_DISABLED"
1 "CONTROL_LOCK_ENABLED"

lockTargetState

Properties

Type: lockTargetState
UUID: 0000001E-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "UNSECURED"
1 "SECURED"

logs

Properties

Type: logs
UUID: 0000001F-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

macRetransmissionMaximum

Properties

Type: macRetransmissionMaximum
UUID: 00000247-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read

macTransmissionCounters

Properties

Type: macTransmissionCounters
UUID: 00000248-0000-1000-8000-0026BB765291
Format: data
Perms: Paired Read

managedNetworkEnable

Properties

Type: managedNetworkEnable
UUID: 00000215-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Paired Read, Paired Write, Events, Timed Write

Constants

Value String
0 "DISABLED"
1 "ENABLED"
2 "UNKNOWN"

manuallyDisabled

Properties

Type: manuallyDisabled
UUID: 00000227-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Events

Constants

Value String
0 "ENABLED"
1 "DISABLED"

manufacturer

Properties

Type: manufacturer
UUID: 00000020-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

model

Properties

Type: model
UUID: 00000021-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

motionDetected

Properties

Type: motionDetected
UUID: 00000022-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

mute

Properties

Type: mute
UUID: 0000011A-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

name

Properties

Type: name
UUID: 00000023-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

networkAccessViolationControl

Properties

Type: networkAccessViolationControl
UUID: 0000021F-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Events, Timed Write, Write Response

networkClientProfileControl

Properties

Type: networkClientProfileControl
UUID: 0000020C-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Events, Timed Write, Write Response

networkClientStatusControl

Properties

Type: networkClientStatusControl
UUID: 0000020D-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Write Response

nightVision

Properties

Type: nightVision
UUID: 0000011B-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events, Timed Write

Constants

Value String
0 "FALSE"
1 "TRUE"

nitrogenDioxideDensity

Properties

Type: nitrogenDioxideDensity
UUID: 000000C4-0000-1000-8000-0026BB765291
Format: float
Max Value: 1000
Min Step: 1
Perms: Paired Read, Events

obstructionDetected

Properties

Type: obstructionDetected
UUID: 00000024-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

occupancyDetected

Properties

Type: occupancyDetected
UUID: 00000071-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "OCCUPANCY_NOT_DETECTED"
1 "OCCUPANCY_DETECTED"

on

Properties

Type: on
UUID: 00000025-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

operatingStateResponse

Properties

Type: operatingStateResponse
UUID: 00000232-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

opticalZoom

Properties

Type: opticalZoom
UUID: 0000011C-0000-1000-8000-0026BB765291
Format: float
Min Step: 0.1
Perms: Paired Read, Paired Write, Events

outletInUse

Properties

Type: outletInUse
UUID: 00000026-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

ozoneDensity

Properties

Type: ozoneDensity
UUID: 000000C3-0000-1000-8000-0026BB765291
Format: float
Max Value: 1000
Min Step: 1
Perms: Paired Read, Events

pm10Density

Properties

Type: pm10Density
UUID: 000000C7-0000-1000-8000-0026BB765291
Format: float
Max Value: 1000
Min Step: 1
Perms: Paired Read, Events

pm2_5Density

Properties

Type: pm2_5Density
UUID: 000000C6-0000-1000-8000-0026BB765291
Format: float
Max Value: 1000
Min Step: 1
Perms: Paired Read, Events

pairSetup

Properties

Type: pairSetup
UUID: 0000004C-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

pairVerify

Properties

Type: pairVerify
UUID: 0000004E-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

pairingFeatures

Properties

Type: pairingFeatures
UUID: 0000004F-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read

pairingPairings

Properties

Type: pairingPairings
UUID: 00000050-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

passwordSetting

Properties

Type: passwordSetting
UUID: 000000E4-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Events, Paired Read, Paired Write

periodicSnapshotsActive

Properties

Type: periodicSnapshotsActive
UUID: 00000225-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "DISABLE"
1 "ENABLE"

pictureMode

Properties

Type: pictureMode
UUID: 000000E2-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 13
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "OTHER"
1 "STANDARD"
2 "CALIBRATED"
3 "CALIBRATED_DARK"
4 "VIVID"
5 "GAME"
6 "COMPUTER"
7 "CUSTOM"
8 "UNKNOWN8"
9 "UNKNOWN9"
10 "UNKNOWN10"
11 "UNKNOWN11"
12 "UNKNOWN12"
13 "UNKNOWN13"

ping

Properties

Type: ping
UUID: 0000023C-0000-1000-8000-0026BB765291
Format: data
Perms: Paired Read

positionState

Properties

Type: positionState
UUID: 00000072-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "DECREASING"
1 "INCREASING"
2 "STOPPED"

powerModeSelection

Properties

Type: powerModeSelection
UUID: 000000DF-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Write

Constants

Value String
0 "SHOW"
1 "HIDE"

productData

Properties

Type: productData
UUID: 00000220-0000-1000-8000-0026BB765291
Format: data
Perms: Paired Read

programMode

Properties

Type: programMode
UUID: 000000D1-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NO_PROGRAM_SCHEDULED"
1 "PROGRAM_SCHEDULED"
2 "PROGRAM_SCHEDULED_MANUAL_MODE"

programmableSwitchEvent

Properties

Type: programmableSwitchEvent
UUID: 00000073-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "SINGLE_PRESS"
1 "DOUBLE_PRESS"
2 "LONG_PRESS"

programmableSwitchOutputState

Properties

Type: programmableSwitchOutputState
UUID: 00000074-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

receivedSignalStrengthIndication

Properties

Type: receivedSignalStrengthIndication
UUID: 0000023F-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read

receiverSensitivity

Properties

Type: receiverSensitivity
UUID: 00000244-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read

recordingAudioActive

Properties

Type: recordingAudioActive
UUID: 00000226-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read, Paired Write, Events, Timed Write

Constants

Value String
0 "DISABLE"
1 "ENABLE"

relativeHumidityDehumidifierThreshold

Properties

Type: relativeHumidityDehumidifierThreshold
UUID: 000000C9-0000-1000-8000-0026BB765291
Format: float
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

relativeHumidityHumidifierThreshold

Properties

Type: relativeHumidityHumidifierThreshold
UUID: 000000CA-0000-1000-8000-0026BB765291
Format: float
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

relayControlPoint

Properties

Type: relayControlPoint
UUID: 0000005E-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Events

relayEnabled

Properties

Type: relayEnabled
UUID: 0000005B-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

relayState

Properties

Type: relayState
UUID: 0000005C-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 5
Min Step: 1
Perms: Paired Read, Events

remainingDuration

Properties

Type: remainingDuration
UUID: 000000D4-0000-1000-8000-0026BB765291
Format: uint32
Max Value: 3600
Min Step: 1
Perms: Paired Read, Events

remoteKey

Properties

Type: remoteKey
UUID: 000000E1-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 16
Min Step: 1
Perms: Paired Write

Constants

Value String
0 "REWIND"
1 "FAST_FORWARD"
2 "NEXT_TRACK"
3 "PREVIOUS_TRACK"
4 "ARROW_UP"
5 "ARROW_DOWN"
6 "ARROW_LEFT"
7 "ARROW_RIGHT"
8 "SELECT"
9 "BACK"
10 "EXIT"
11 "PLAY_PAUSE"
12 "UNKNOWN12"
13 "UNKNOWN13"
14 "UNKNOWN14"
15 "INFORMATION"
16 "UNKNOWN16"

resetFilterIndication

Properties

Type: resetFilterIndication
UUID: 000000AD-0000-1000-8000-0026BB765291
Format: uint8
Min Value: 1
Max Value: 1
Min Step: 1
Perms: Paired Write

rotationDirection

Properties

Type: rotationDirection
UUID: 00000028-0000-1000-8000-0026BB765291
Format: int
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "CLOCKWISE"
1 "COUNTER_CLOCKWISE"

rotationSpeed

Properties

Type: rotationSpeed
UUID: 00000029-0000-1000-8000-0026BB765291
Format: float
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

routerStatus

Properties

Type: routerStatus
UUID: 0000020E-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Paired Read, Events

Constants

Value String
0 "READY"
1 "NOT_READY"

saturation

Properties

Type: saturation
UUID: 0000002F-0000-1000-8000-0026BB765291
Format: float
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

securitySystemAlarmType

Properties

Type: securitySystemAlarmType
UUID: 0000008E-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

securitySystemCurrentState

Properties

Type: securitySystemCurrentState
UUID: 00000066-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 4
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "STAY_ARM"
1 "AWAY_ARM"
2 "NIGHT_ARM"
3 "DISARMED"
4 "ALARM_TRIGGERED"

securitySystemTargetState

Properties

Type: securitySystemTargetState
UUID: 00000067-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "STAY_ARM"
1 "AWAY_ARM"
2 "NIGHT_ARM"
3 "DISARM"

selectedAudioStreamConfiguration

Properties

Type: selectedAudioStreamConfiguration
UUID: 00000128-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

selectedCameraRecordingConfiguration

Properties

Type: selectedCameraRecordingConfiguration
UUID: 00000209-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Events

selectedRTPStreamConfiguration

Properties

Type: selectedRTPStreamConfiguration
UUID: 00000117-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

serialNumber

Properties

Type: serialNumber
UUID: 00000030-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

serviceLabelIndex

Properties

Type: serviceLabelIndex
UUID: 000000CB-0000-1000-8000-0026BB765291
Format: uint8
Min Value: 1
Max Value: 255
Min Step: 1
Perms: Paired Read

serviceLabelNamespace

Properties

Type: serviceLabelNamespace
UUID: 000000CD-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read

Constants

Value String
0 "DOTS"
1 "ARABIC_NUMERALS"

setDuration

Properties

Type: setDuration
UUID: 000000D3-0000-1000-8000-0026BB765291
Format: uint32
Max Value: 3600
Min Step: 1
Perms: Paired Read, Paired Write, Events

setupDataStreamTransport

Properties

Type: setupDataStreamTransport
UUID: 00000131-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Write Response

setupEndpoints

Properties

Type: setupEndpoints
UUID: 00000118-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

setupTransferTransport

Properties

Type: setupTransferTransport
UUID: 00000201-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Write, Write Response

signalToNoiseRatio

Properties

Type: signalToNoiseRatio
UUID: 00000241-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read

siriInputType

Properties

Type: siriInputType
UUID: 00000132-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read

Constants

Value String
0 "PUSH_BUTTON_TRIGGERED_APPLE_TV"

slatType

Properties

Type: slatType
UUID: 000000C0-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read

Constants

Value String
0 "HORIZONTAL"
1 "VERTICAL"

sleepDiscoveryMode

Properties

Type: sleepDiscoveryMode
UUID: 000000E8-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NOT_DISCOVERABLE"
1 "ALWAYS_DISCOVERABLE"

sleepInterval

Properties

Type: sleepInterval
UUID: 0000023A-0000-1000-8000-0026BB765291
Format: uint32
Min Step: 1
Perms: Paired Read, Events

smokeDetected

Properties

Type: smokeDetected
UUID: 00000076-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "SMOKE_NOT_DETECTED"
1 "SMOKE_DETECTED"

softwareRevision

Properties

Type: softwareRevision
UUID: 00000054-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

statusActive

Properties

Type: statusActive
UUID: 00000075-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Read, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

statusFault

Properties

Type: statusFault
UUID: 00000077-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NO_FAULT"
1 "GENERAL_FAULT"

statusJammed

Properties

Type: statusJammed
UUID: 00000078-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NOT_JAMMED"
1 "JAMMED"

statusLowBattery

Properties

Type: statusLowBattery
UUID: 00000079-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "BATTERY_LEVEL_NORMAL"
1 "BATTERY_LEVEL_LOW"

statusTampered

Properties

Type: statusTampered
UUID: 0000007A-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NOT_TAMPERED"
1 "TAMPERED"

streamingStatus

Properties

Type: streamingStatus
UUID: 00000120-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

sulphurDioxideDensity

Properties

Type: sulphurDioxideDensity
UUID: 000000C5-0000-1000-8000-0026BB765291
Format: float
Max Value: 1000
Min Step: 1
Perms: Paired Read, Events

supportedAudioRecordingConfiguration

Properties

Type: supportedAudioRecordingConfiguration
UUID: 00000207-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

supportedAudioStreamConfiguration

Properties

Type: supportedAudioStreamConfiguration
UUID: 00000115-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

supportedCameraRecordingConfiguration

Properties

Type: supportedCameraRecordingConfiguration
UUID: 00000205-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

supportedDataStreamTransportConfiguration

Properties

Type: supportedDataStreamTransportConfiguration
UUID: 00000130-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

supportedCharacteristicValueTransitionConfiguration

Properties

Type: supportedCharacteristicValueTransitionConfiguration
UUID: 00000144-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

supportedDiagnosticsSnapshot

Properties

Type: supportedDiagnosticsSnapshot
UUID: 00000238-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

supportedRTPConfiguration

Properties

Type: supportedRTPConfiguration
UUID: 00000116-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

supportedRouterConfiguration

Properties

Type: supportedRouterConfiguration
UUID: 00000210-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

supportedTransferTransportConfiguration

Properties

Type: supportedTransferTransportConfiguration
UUID: 00000202-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

supportedVideoRecordingConfiguration

Properties

Type: supportedVideoRecordingConfiguration
UUID: 00000206-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

supportedVideoStreamConfiguration

Properties

Type: supportedVideoStreamConfiguration
UUID: 00000114-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

swingMode

Properties

Type: swingMode
UUID: 000000B6-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "SWING_DISABLED"
1 "SWING_ENABLED"

targetAirPurifierState

Properties

Type: targetAirPurifierState
UUID: 000000A8-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "MANUAL"
1 "AUTO"

targetControlList

Properties

Type: targetControlList
UUID: 00000124-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Write, Paired Read, Write Response

targetControlSupportedConfiguration

Properties

Type: targetControlSupportedConfiguration
UUID: 00000123-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

targetDoorState

Properties

Type: targetDoorState
UUID: 00000032-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "OPEN"
1 "CLOSED"

targetFanState

Properties

Type: targetFanState
UUID: 000000BF-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "MANUAL"
1 "AUTO"

targetHeaterCoolerState

Properties

Type: targetHeaterCoolerState
UUID: 000000B2-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "AUTO"
1 "HEAT"
2 "COOL"

targetHeatingCoolingState

Properties

Type: targetHeatingCoolingState
UUID: 00000033-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "OFF"
1 "HEAT"
2 "COOL"
3 "AUTO"

targetHorizontalTiltAngle

Properties

Type: targetHorizontalTiltAngle
UUID: 0000007B-0000-1000-8000-0026BB765291
Format: int
Unit: arcdegrees
Min Value: -90
Max Value: 90
Min Step: 1
Perms: Paired Read, Paired Write, Events

targetHumidifierDehumidifierState

Properties

Type: targetHumidifierDehumidifierState
UUID: 000000B4-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "HUMIDIFIER_OR_DEHUMIDIFIER"
1 "HUMIDIFIER"
2 "DEHUMIDIFIER"

targetMediaState

Properties

Type: targetMediaState
UUID: 00000137-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "PLAY"
1 "PAUSE"
2 "STOP"

targetPosition

Properties

Type: targetPosition
UUID: 0000007C-0000-1000-8000-0026BB765291
Format: uint8
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

targetRelativeHumidity

Properties

Type: targetRelativeHumidity
UUID: 00000034-0000-1000-8000-0026BB765291
Format: float
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

targetTemperature

Properties

Type: targetTemperature
UUID: 00000035-0000-1000-8000-0026BB765291
Format: float
Unit: celsius
Min Value: 10
Max Value: 38
Min Step: 0.1
Perms: Paired Read, Paired Write, Events

targetTiltAngle

Properties

Type: targetTiltAngle
UUID: 000000C2-0000-1000-8000-0026BB765291
Format: int
Unit: arcdegrees
Min Value: -90
Max Value: 90
Min Step: 1
Perms: Paired Read, Paired Write, Events

targetVerticalTiltAngle

Properties

Type: targetVerticalTiltAngle
UUID: 0000007D-0000-1000-8000-0026BB765291
Format: int
Unit: arcdegrees
Min Value: -90
Max Value: 90
Min Step: 1
Perms: Paired Read, Paired Write, Events

targetVisibilityState

Properties

Type: targetVisibilityState
UUID: 00000134-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "SHOWN"
1 "HIDDEN"

temperatureDisplayUnits

Properties

Type: temperatureDisplayUnits
UUID: 00000036-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Read, Paired Write, Events

Constants

Value String
0 "CELSIUS"
1 "FAHRENHEIT"

thirdPartyCameraActive

Properties

Type: thirdPartyCameraActive
UUID: 0000021C-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read, Events

Constants

Value String
0 "OFF"
1 "ON"

transmitPower

Properties

Type: transmitPower
UUID: 00000242-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read

MaximumTransmitPower

Properties

Type: MaximumTransmitPower
UUID: 00000243-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read

tunnelConnectionTimeout

Properties

Type: tunnelConnectionTimeout
UUID: 00000061-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Write, Paired Read, Events

tunneledAccessoryAdvertising

Properties

Type: tunneledAccessoryAdvertising
UUID: 00000060-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Write, Paired Read, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

tunneledAccessoryConnected

Properties

Type: tunneledAccessoryConnected
UUID: 00000059-0000-1000-8000-0026BB765291
Format: bool
Perms: Paired Write, Paired Read, Events

Constants

Value String
0 "FALSE"
1 "TRUE"

tunneledAccessoryStateNumber

Properties

Type: tunneledAccessoryStateNumber
UUID: 00000058-0000-1000-8000-0026BB765291
Format: int
Perms: Paired Read, Events

vocDensity

Properties

Type: vocDensity
UUID: 000000C8-0000-1000-8000-0026BB765291
Format: float
Max Value: 1000
Min Step: 1
Perms: Paired Read, Events

valveType

Properties

Type: valveType
UUID: 000000D5-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "GENERIC_VALVE"
1 "IRRIGATION"
2 "SHOWER_HEAD"
3 "WATER_FAUCET"

version

Properties

Type: version
UUID: 00000037-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

videoAnalysisActive

Properties

Type: videoAnalysisActive
UUID: 00000229-0000-1000-8000-0026BB765291
Format: uint8
Perms: Paired Read, Paired Write, Events

volume

Properties

Type: volume
UUID: 00000119-0000-1000-8000-0026BB765291
Format: uint8
Unit: percentage
Max Value: 100
Min Step: 1
Perms: Paired Read, Paired Write, Events

volumeControlType

Properties

Type: volumeControlType
UUID: 000000E9-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 3
Min Step: 1
Perms: Paired Read, Events

Constants

Value String
0 "NONE"
1 "RELATIVE"
2 "RELATIVE_WITH_CURRENT"
3 "ABSOLUTE"

volumeSelector

Properties

Type: volumeSelector
UUID: 000000EA-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Min Step: 1
Perms: Paired Write

Constants

Value String
0 "INCREMENT"
1 "DECREMENT"

wanConfigurationList

Properties

Type: wanConfigurationList
UUID: 00000211-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

wanStatusList

Properties

Type: wanStatusList
UUID: 00000212-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

wakeConfiguration

Properties

Type: wakeConfiguration
UUID: 00000222-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

waterLevel

Properties

Type: waterLevel
UUID: 000000B5-0000-1000-8000-0026BB765291
Format: float
Max Value: 100
Min Step: 1
Perms: Paired Read, Events

wifiCapabilities

Properties

Type: wifiCapabilities
UUID: 0000022C-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read

wifiConfigurationControl

Properties

Type: wifiConfigurationControl
UUID: 0000022D-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Events, Timed Write, Write Response

wifiSatelliteStatus

Properties

Type: wifiSatelliteStatus
UUID: 0000021E-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 2
Perms: Paired Read, Events

Constants

Value String
0 "UNKNOWN"
1 "CONNECTED"
2 "NOT_CONNECTED"

assetUpdateReadiness

Properties

Type: assetUpdateReadiness
UUID: 00000269-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read, Events

supportedAssetTypes

Properties

Type: supportedAssetTypes
UUID: 00000268-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read

configurationState

Properties

Type: configurationState
UUID: 00000263-0000-1000-8000-0026BB765291
Format: uint16
Perms: Paired Read, Events

NFCAccessControlPoint

Properties

Type: NFCAccessControlPoint
UUID: 00000264-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Write Response

NFCAccessSupportedConfiguration

Properties

Type: NFCAccessSupportedConfiguration
UUID: 00000265-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

SiriEndpointSessionStatus

Properties

Type: SiriEndpointSessionStatus
UUID: 00000254-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Events

threadControlPoint

Properties

Type: threadControlPoint
UUID: 00000704-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Write

threadNodeCapabilities

Properties

Type: threadNodeCapabilities
UUID: 00000702-0000-1000-8000-0026BB765291
Format: uint16
Max Value: 31
Min Step: 1
Perms: Paired Read

threadStatus

Properties

Type: threadStatus
UUID: 00000703-0000-1000-8000-0026BB765291
Format: uint16
Max Value: 6
Min Step: 1
Perms: Paired Read, Events

threadOpenThreadVersion

Properties

Type: threadOpenThreadVersion
UUID: 00000706-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

accessCodeControlPoint

Properties

Type: accessCodeControlPoint
UUID: 00000262-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write, Write Response

accessCodeSupportedConfiguration

Properties

Type: accessCodeSupportedConfiguration
UUID: 00000261-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

airPlayEnable

Properties

Type: airPlayEnable
UUID: 0000025B-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Events, Paired Read, Paired Write

characteristicValueActiveTransitionCount

Properties

Type: characteristicValueActiveTransitionCount
UUID: 0000024B-0000-1000-8000-0026BB765291
Format: uint8
Perms: Events, Paired Read

cryptoHash

Properties

Type: cryptoHash
UUID: 00000250-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Write, Write Response

firmwareUpdateReadiness

Properties

Type: firmwareUpdateReadiness
UUID: 00000234-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Events, Paired Read

firmwareUpdateStatus

Properties

Type: firmwareUpdateStatus
UUID: 00000235-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Events, Paired Read

hardwareFinish

Properties

Type: hardwareFinish
UUID: 0000026C-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

MetricsBufferFullState

Properties

Type: MetricsBufferFullState
UUID: 00000272-0000-1000-8000-0026BB765291
Format: bool
Perms: Events, Paired Read

multiFunctionButton

Properties

Type: multiFunctionButton
UUID: 0000026B-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Events, Paired Read

selectedDiagnosticsModes

Properties

Type: selectedDiagnosticsModes
UUID: 0000024D-0000-1000-8000-0026BB765291
Format: uint32
Perms: Paired Read, Paired Write

selectedSleepConfiguration

Properties

Type: selectedSleepConfiguration
UUID: 00000252-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Events, Paired Read, Paired Write, Write Response

siriEnable

Properties

Type: siriEnable
UUID: 00000255-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Events, Paired Read, Paired Write

siriEngineVersion

Properties

Type: siriEngineVersion
UUID: 0000025A-0000-1000-8000-0026BB765291
Format: string
Perms: Paired Read

siriLightOnUse

Properties

Type: siriLightOnUse
UUID: 00000258-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Events, Paired Read, Paired Write

siriListening

Properties

Type: siriListening
UUID: 00000256-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Events, Paired Read, Paired Write

siriTouchToUse

Properties

Type: siriTouchToUse
UUID: 00000257-0000-1000-8000-0026BB765291
Format: uint8
Max Value: 1
Perms: Events, Paired Read, Paired Write

StagedFirmwareVersion

Properties

Type: StagedFirmwareVersion
UUID: 00000249-0000-1000-8000-0026BB765291
Format: string
Perms: Events, Paired Read

supportedMetrics

Properties

Type: supportedMetrics
UUID: 00000271-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read, Paired Write

supportedFirmwareUpdateConfiguration

Properties

Type: supportedFirmwareUpdateConfiguration
UUID: 00000233-0000-1000-8000-0026BB765291
Format: tlv8
Perms: Paired Read

tapType

Properties

Type: tapType
UUID: 0000022F-0000-1000-8000-0026BB765291
Format: uint16
Perms: Paired Read

token

Properties

Type: token
UUID: 00000231-0000-1000-8000-0026BB765291
Format: data
Perms: Paired Write

CMD4 Directives

These directives appear within the "Cmd4" plugin header and reflect how Cmd4 will behave overall.

Cmd4DirectiveTypeDefaultDescription
debug < Bool > false Output Debug messages. Can also be accomplished with setenv DEBUG=Cmd4
outputConstants < Bool > false If Cmd4 will send Strings like "TRUE" or "FALSE" instead of 0 | 1 for all Accessories
statusMsg < Bool > TRUE Cmd4 will log a Set Status Message for all Accessories.
interval interval 60 < seconds > How long before killing the state command of all polled characteristics. See interval in Accessory Directives for full description as it can also be set per accessory or per characteristic.
timeout timeout 60000 < msec > How long before killing the state command of all polled characteristics. See timeout in Accessory Directives for full description as it can also be set per accessory or per characteristic.
queueTypes < Array of { "queue": < "SomeName" >, "queueType": < "Sequential" | "WoRm" > "retries": < 0 > } none Pre-defines Queue types. This Directive can also be per queue as noted by its presence as an accessory directive.
definitions < Array > An Array of new Characteristic Objects. See Custom Characteristics, around line 339 for more information

CMD4 Accessory Directives

These directives appear within the characteristic section of the Cmd4 Accessory

Cmd4DirectiveTypeDefaultDescription
subType < unique string > accessory.name Used to distinguish accessories that have same Type and Name.
outputConstants < Bool > false If Cmd4 will send Strings like "TRUE" or "FALSE" instead of 0 | 1
statusMsg < Bool > TRUE Cmd4 will log a Set Status Message
publishExternally < Bool > false Tell Homebridge to publish the device as its own bridge.
stateChangeResponseTime < seconds > 60 How long to wait between a Set and Get command.
interval < seconds > 60 < seconds > How long between polls of all polled characteristics.
timeout < msec > 60000 < msec > How long before killing the state command of all polled characteristics.
polling < Bool > false If the characteristics should be polled for state change.
or [{"characteristic" < characteristic >, [ "interval": < sec >, "timeout": < msec > ] }] The timeout and interval override any higher directive.
queue queue < String > Defines which Queue polled characteristics will be placed.
state_cmd < state_cmd > See Description The command used to Get/Set Device characteristic State.
The state_cmd represents the path to your script. Cmd4 appends the Get/Set shown.
state_cmd Get < DisplayName > < characteristic >
state_cmd Set < DisplayName > < characteristic > < value >
state_cmd_prefix < String > A String prepended to the < state_cmd >.
state_cmd_suffix < String > A String appended to the < state_cmd >.
props < Bool > false A way to override Hap Characteristiic Properties
Only used to set min/max temperatures, for instance:
"props" : { "currentTemperature": { "maxValue":100, "minValue": -100, "minStep": 0.1}}
category < CATEGORY > See Homebridge Categories for a complete list of possible categories.
fakegato < JSON > See the section, "Adding in Fakegato history" below.
linkedTypes < JSON > Other Cmd4 Accessories like Input Source for HDMI inputs.

Custom Characteristics

These directives appear within the Cmd4 Platform section


IMPORTANT - It is unlikely that adding custom characteristics will be supported by any GUI or SIRI


Custom characteristics in Cmd4 do not stray far from how Homebridge uses them from HAP-NodeJS. To create a custom characteristic an object entry is programmatically added to lib/CMD4_ACC_TYPE_ENUM.js via an entry you define in your config.json. The best way to show how to define a custom characteristic is through an example

.
            {
               "platform": "Cmd4",
               "outputConstants": false,
               "statusMsg": true,
               "Definitions": [
                  { "type": "PointX",
                    "description": "An X Coordinate",
                    "props": { "format": "uint32",
                               "minValue": 0,
                               "minStep": 1,
                               "perms": [ "ev", "pr", "pw" ]
                             }
                  }
               ],
               "accessories": [
                  { "type": "Switch",
                    "name": "PS_4",
                    "PointX": 3267,
                    "on": false,
                    "polling": [ { "characteristic": "on", "interval": 50 },
                                 { "characteristic": "pointX", "interval": 5 }
                               ],
                    "state_cmd": "node .homebridge/Cmd4Scripts/AnyDevice.js"
                  }
               ]
            }
         


The definitions for "props" can be found at: HAP Characteristics - line 259

Example Cmd4 JavaScript Template

This script is auto generated from: ./Extras/Cmd4Scripts/Examples/ExampleJavaScript_template.js

#!/usr/bin/env node
//  ExampleScript_template.js
//
// Description:
//   This script is a goood starting place for you to create Cmd4 Scripts
//   of your own
//
// Parameters are:
//    Get < Any accessory name > < Characteristic >
//    Set < Any accessory name > < Characteristic > < value >
//
// Note 1: These paramaters match the those of the Cmd4 plugin.
//         A full lost of supported devices and characteristics can be
//         found at:
//         https://ztalbot2000.github.io/homebridge-cmd4
//
// How it works:
//    The Cmd4 plugin will call this script to retrieve those states
//    you have defined as not Cached to Get/Set your devices characteristic
//    states.
//
//    For example:
//       node ExampleScript_template.js Set My_Door TargetDoorState 0
//     or
//       node ExampleScript.js Get My_Door CurrentDoorState
//
//

'use strict';

var length = process.argv.length;
var device = "My_Door";
var io = "";
var characteristic = "";
var option = "";

if ( length == 2 ) process.exit( 0 );

if ( length <= 2 ) {
    console.log( "Usage: " + process.argv[0] + " < Get > < AccessoryName > < Characteristic >" );
    console.log( "       " + process.argv[0] + " < Set > < AccessoryName > < Characteristic > < Value >" );
    process.exit( -1 );
}

if ( length >= 2 ) io = process.argv[2];
if ( length >= 3 ) device = process.argv[3];
if ( length >= 4 ) characteristic  = process.argv[4];
if ( length >= 5 ) option  = process.argv[5];

var c = "";

switch( io )
{
    case "Get":
    {
        switch( characteristic )
        {
            case "CurrentDoorState":
            {
               console.log( 0 );

               // See https://ztalbot2000.github.io/homebridge-cmd4
               // For the possible values and characteristics
               // available per device. It will show somethink like:
               // Valid Values:
               // 0 - "Open. The door is fully open."
               // 1 - "Closed. The door is fully closed."
               // 2 - "Opening. The door is actively opening."
               // 3 - "Closing. The door is actively closing."
               // 4 - "Stopped. The door is not moving, and it is not fully
               //      open nor fully closed."
               // 5-255 - "Reserved"

               break;
            }
            case "TargetDoorState":
            {
                console.log( 0 );
                break;
            }
            case "ObstructionDetected":
            {
                console.log ( 0 );
                break;
            }
            case "LockCurrentState":
            {
                console.log ( 0 );
                break;
            }
            default:
                console.error( "Unhandled characteristic for:"  + io  +  " Device:" + device  +  " Characteristic:" + characteristic );
                process.exit( -1 );
        }

        break;

    } // End of Switch for "Get"
    case "Set":
    {
        switch( characteristic )
        {
            case "CurrentDoorState":
            {
               // Current Door State is not settable. The
               // call would be to TargetDoorState. This is here
               // for debugging only.

               break;
            }
            case "TargetDoorState":
            {
                // Do something of your own here.

                break;
            }
            case "ObstructionDetected":
            {
               // Obstruction Detected is not settable. It
               // call is a read-only characteristic. This is here
               // for debugging only.
               break;
            }
            case "LockCurrentState":
            {
                // Lock Current State is not settable. It
                // call is a read-only characteristic. This is here
                // for debugging only.
                break;
            }
            default:
                console.error( "UnHandled Characteristic for:"  + io  +  " Device:" + device  +  " Characteristic:" + characteristic );
                process.exit( -1 );
        }

        break;
    } // End of Switch Device for "Set"
    default:
        console.error( "Unknown IO" + io );
        process.exit( -1 );
}

//console.log( "Say What Device:" + device + " Characteristic:" + characteristic + " Option:" + option );

// You must exit with a zero status, confirming the script rannsuccessfully.
process.exit( 0 );


         

Example Cmd4 Shell Script Template

This script is auto generated from: ./Extras/Cmd4Scripts/Examples/ExampleShellScript_template.sh

#!/bin/bash

#  ExampleScript_template.sh
#
# Description:
#   This script is a goood starting place for you to create Cmd4 Scripts
#   of your own
#
# Parameters are:
#    Get < Any accessory name > < Characteristic>
#    Set < Any accessory name > < Characteristic> < value >
#
# Note 1: These paramaters match the those of the Cmd4 plugin.
#         A full lost of supported devices and characteristics can be
#         found at:
#         https://ztalbot2000.github.io/homebridge-cmd4
#
# How it works:
#
#    The Cmd4 plugin will call this script to retrieve those states
#    you have defined as not Cached to Get/Set your devices characteristic
#    states.
#
#    For example:
#       bash ExampleScript_template.sh Set My_Door TargetDoorState 0
#     or
#       bash ExampleScript.sh Get My_Door CurrentDoorState
#

set -e

# Exit immediately for unbound variables.
set -u


length=$#
device=""
io=""
characteristic=""
option=""

if [ $length -le 1 ]; then
   printf "Usage: $0 Get < AccessoryName > < Characteristic >\n"
   printf "Usage: $0 Set < AccessoryName > < Characteristic > < Value >\n"
   exit -1
fi

# printf "args =$#\n"   # debug
# printf "arg1 =$1\n"   # debug

if [ $length -ge 1 ]; then
    io=$1
    # printf "io=$io\n"   # debug
fi
if [ $length -ge 2 ]; then
    device=$2
    # printf "device = ${device}\n"   # debug
fi
if [ $length -ge 3 ]; then
    characteristic=$3
    # printf "Characteristic = ${characteristic}\n"   # debug
fi
if [ $length -ge 4 ]; then
    option=$4
    # printf "option = ${option}\n"   # debug
fi

if [ "${io}" == "Get" ]; then
   case $characteristic in
      'CurrentDoorState')

         printf "0\n"          # Door is open

         # See https://ztalbot2000.github.io/homebridge-cmd4
         # For the possible values and characteristics
         # available per device. It will show somethink like:
         # Valid Values:
         # 0 - "Open. The door is fully open."
         # 1 - "Closed. The door is fully closed."
         # 2 - "Opening. The door is actively opening."
         # 3 - "Closing. The door is actively closing."
         # 4 - "Stopped. The door is not moving, and it is not fully
         #      open nor fully closed."
         # 5-255 - "Reserved"
         exit 0
         ;;
      'TargetDoorState')
         printf "0\n"
         exit 0
         ;;
      'ObstructionDetected')
         printf "0\n"
         exit 0
         ;;
      'LockCurrentState')
         printf "0\n"
         exit 0
         ;;
      *)
         printf "UnHandled Get ${device}  Characteristic ${characteristic}\n"
         exit -1
         ;;
    esac
fi
if [ "${io}" == 'Set' ]; then
   case $characteristic in
      'CurrentDoorState')
         # Current Door State is not settable. The
         # call would be to TargetDoorState. This is here
         # for debugging only.

         exit 0
         ;;
      'TargetDoorState')
         # Do something of your own here.
         exit 0
         ;;
      'ObstructionDetected')
         # Obstruction Detected is not settable. It
         # call is a read-only characteristic. This is here
         # for debugging only.
         exit 0
         ;;
      'LockCurrentState')
         # Lock Current State is not settable. It
         # call is a read-only characteristic. This is here
         # for debugging only.
         exit 0
         ;;
      *)
         printf "UnHandled Set GarageDoorOpenner Characteristic ${characteristic}"
         exit -1
         ;;
    esac
fi
printf "Unknown io command ${io}\n"
exit -1


         
================================================ FILE: docs/index.html ================================================

Please follow this link.

================================================ FILE: index.js ================================================ "use strict"; // // Homebridge // Flow / \ // / \ // api.registerPlatform api.registerAccessory // forEach Accessories{ } Any { } before/after Accessories{ } // Cmd4Platform Cmd4Accessory // Cmd4Accessory // // // // // // // // // // // The Cmd4 Classes const Cmd4Accessory = require( "./Cmd4Accessory" ).Cmd4Accessory; const Cmd4Platform = require( "./Cmd4Platform" ).Cmd4Platform; const settings = require( "./cmd4Settings" ); // Pretty colors const chalk = require( "chalk" ); // The Library files that know all. var CHAR_DATA = require( "./lib/CMD4_CHAR_TYPE_ENUMS" ); var ACC_DATA = require( "./lib/CMD4_ACC_TYPE_ENUM" ); var DEVICE_DATA = require( "./lib/CMD4_DEVICE_TYPE_ENUM" ); module.exports = { default: function ( api ) { // Init the libraries for all to use let CMD4_CHAR_TYPE_ENUMS = CHAR_DATA.init( api.hap.Formats, api.hap.Units, api.hap.Perms ); let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( api.hap.Characteristic, api.hap.Formats, api.hap.Units, api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, api.hap.Service, api.hap.Characteristic, api.hap.Categories ); api.registerAccessory( settings.PLATFORM_NAME, Cmd4Accessory ); api.registerPlatform( settings.PLATFORM_NAME, Cmd4Platform ); setTimeout( checkForUpdates, 1800 ); // This is not required by homebridge and does not affect it. I use it for // unit testing. return { CMD4_CHAR_TYPE_ENUMS, CMD4_ACC_TYPE_ENUM, CMD4_DEVICE_TYPE_ENUM, api }; }, // These would be the uninitialized values, // used for unit testing CHAR_DATA: CHAR_DATA, // properties would be { } empty. ACC_DATA: ACC_DATA, // properties would be { } empty. DEVICE_DATA: DEVICE_DATA // properties would be { } empty. } function checkForUpdates( ) { // Don't show the updates message in mocha test mode if ( process.argv.includes( "test/mocha-setup" ) ) return; const { getLatestVersion, isVersionNewerThanPackagedVersion } = require( "./utils/versionChecker" ); const myPkg = require( "./package.json" ); ( async( ) => { // Fix for #127, constant crash loops when no internet connection // trying to get latest Cmd4 version. // thx nano9g try { let lv = await getLatestVersion( ); if ( isVersionNewerThanPackagedVersion( lv ) ) { console.log( chalk.green( `[UPDATE AVAILABLE] ` ) + `Version ${lv} of ${myPkg.name} is available. Any release notes can be found here: ` + chalk.underline( `${myPkg.changelog}` ) ); } } catch( error ) { console.log( chalk.yellow( `[UPDATE CHECK FAILED] ` ) + `Could not check for newer versions of ${myPkg.name} due to error ${error.name}: ${error.message}`) } })( ); } ================================================ FILE: lib/CMD4_ACC_TYPE_ENUM.js ================================================ 'use strict'; // The sObject.defineProperty is to resolve a lint issue. // See utils/indexOfEnumLintTest.js for further information. let indexOfEnum = require( "../utils/indexOfEnum" ); Object.defineProperty( exports, "indexOfEnum", { enumerable: true, get: function ( ){ return indexOfEnum.indexOfEnum; } }); function stringToBool( str ) { str += ''; str = str.toUpperCase( ); if ( str == "0" || str == "FALSE" ) return false; if ( str == "1" || str == "TRUE" ) return true; return undefined; } function stringToFloat( str ) { if ( !isNaN( str ) ) { let rc = parseFloat( str ); if ( ! isNaN( rc ) ) return rc; } return undefined; } function stringToInt( str ) { str += ''; // Convert it to a Number, no matter what let result = Number( str ); if ( typeof result == "number" ) return result; return undefined; } function stringToString( str ) { if ( typeof str == "string" ) return str; // more or less to pass previous testcases if ( typeof str == "number" ) return str += ''; return undefined; } function stringPassThru( str ) { return str; } function stringToTLV8( str ) { return str; } class CustomCharacteristic { constructor( api, type, UUID, props ) { this.characteristic = { }; this.api = api; var _a; this.characteristic[ type ] = (_a = class extends this.api.hap.Characteristic { constructor() { super(type, UUID, props); this.value = this.getDefaultValue(); } }, _a.UUID = UUID, _a); } } // NOTE - DO NOT CHANGE TO LOWER CASE // - This would mean ALL testcases would have to be changed as well. //:76,375s/^ \([A-Z]\)/ \l\1/ var CMD4_ACC_TYPE_ENUM = { AccessControlLevel: 0, AccessoryFlags: 1, AccessoryIdentifier: 2, Active: 3, ActiveIdentifier: 4, ActivityInterval: 5, AdministratorOnlyAccess: 6, AirParticulateDensity: 7, AirParticulateSize: 8, AirQuality: 9, AppMatchingIdentifier: 10, AudioFeedback: 11, BatteryLevel: 12, Brightness: 13, ButtonEvent: 14, CCAEnergyDetectThreshold: 15, CCASignalDetectThreshold: 16, CameraOperatingModeIndicator: 17, CarbonDioxideDetected: 18, CarbonDioxideLevel: 19, CarbonDioxidePeakLevel: 20, CarbonMonoxideDetected: 21, CarbonMonoxideLevel: 22, CarbonMonoxidePeakLevel: 23, Category: 24, CharacteristicValueTransitionControl: 25, ChargingState: 26, ClosedCaptions: 27, ColorTemperature: 28, ConfigureBridgedAccessory: 29, ConfigureBridgedAccessoryStatus: 30, ConfiguredName: 31, ContactSensorState: 32, CoolingThresholdTemperature: 33, CurrentAirPurifierState: 34, CurrentAmbientLightLevel: 35, CurrentDoorState: 36, CurrentFanState: 37, CurrentHeaterCoolerState: 38, CurrentHeatingCoolingState: 39, CurrentHorizontalTiltAngle: 40, CurrentHumidifierDehumidifierState: 41, CurrentMediaState: 42, CurrentPosition: 43, CurrentRelativeHumidity: 44, CurrentSlatState: 45, CurrentTemperature: 46, CurrentTiltAngle: 47, CurrentTime: 48, CurrentTransport: 49, CurrentVerticalTiltAngle: 50, CurrentVisibilityState: 51, DataStreamHAPTransport: 52, DataStreamHAPTransportInterrupt: 53, DayoftheWeek: 54, DiagonalFieldOfView: 55, DigitalZoom: 56, DiscoverBridgedAccessories: 57, DiscoveredBridgedAccessories: 58, DisplayOrder: 59, EventRetransmissionMaximum: 60, EventSnapshotsActive: 61, EventTransmissionCounters: 62, FilterChangeIndication: 63, FilterLifeLevel: 64, FirmwareRevision: 65, HardwareRevision: 66, HeartBeat: 67, HeatingThresholdTemperature: 68, HoldPosition: 69, HomeKitCameraActive: 70, Hue: 71, Identifier: 72, Identify: 73, ImageMirroring: 74, ImageRotation: 75, InUse: 76, InputDeviceType: 77, InputSourceType: 78, IsConfigured: 79, LeakDetected: 80, LinkQuality: 81, ListPairings: 82, LockControlPoint: 83, LockCurrentState: 84, LockLastKnownAction: 85, LockManagementAutoSecurityTimeout: 86, LockPhysicalControls: 87, LockTargetState: 88, Logs: 89, MACRetransmissionMaximum: 90, MACTransmissionCounters: 91, ManagedNetworkEnable: 92, ManuallyDisabled: 93, Manufacturer: 94, Model: 95, MotionDetected: 96, Mute: 97, Name: 98, NetworkAccessViolationControl: 99, NetworkClientProfileControl: 100, NetworkClientStatusControl: 101, NightVision: 102, NitrogenDioxideDensity: 103, ObstructionDetected: 104, OccupancyDetected: 105, On: 106, OperatingStateResponse: 107, OpticalZoom: 108, OutletInUse: 109, OzoneDensity: 110, PM10Density: 111, PM2_5Density: 112, PairSetup: 113, PairVerify: 114, PairingFeatures: 115, PairingPairings: 116, PasswordSetting: 117, PeriodicSnapshotsActive: 118, PictureMode: 119, Ping: 120, PositionState: 121, PowerModeSelection: 122, ProductData: 123, ProgramMode: 124, ProgrammableSwitchEvent: 125, ProgrammableSwitchOutputState: 126, Reachable: 127, ReceivedSignalStrengthIndication: 128, ReceiverSensitivity: 129, RecordingAudioActive: 130, RelativeHumidityDehumidifierThreshold: 131, RelativeHumidityHumidifierThreshold: 132, RelayControlPoint: 133, RelayEnabled: 134, RelayState: 135, RemainingDuration: 136, RemoteKey: 137, ResetFilterIndication: 138, RotationDirection: 139, RotationSpeed: 140, RouterStatus: 141, Saturation: 142, SecuritySystemAlarmType: 143, SecuritySystemCurrentState: 144, SecuritySystemTargetState: 145, SelectedAudioStreamConfiguration: 146, SelectedCameraRecordingConfiguration: 147, SelectedRTPStreamConfiguration: 148, SerialNumber: 149, ServiceLabelIndex: 150, ServiceLabelNamespace: 151, SetDuration: 152, SetupDataStreamTransport: 153, SetupEndpoints: 154, SetupTransferTransport: 155, SignalToNoiseRatio: 156, SiriInputType: 157, SlatType: 158, SleepDiscoveryMode: 159, SleepInterval: 160, SmokeDetected: 161, SoftwareRevision: 162, StatusActive: 163, StatusFault: 164, StatusJammed: 165, StatusLowBattery: 166, StatusTampered: 167, StreamingStatus: 168, SulphurDioxideDensity: 169, SupportedAudioRecordingConfiguration: 170, SupportedAudioStreamConfiguration: 171, SupportedCameraRecordingConfiguration: 172, SupportedCharacteristicValueTransitionConfiguration: 173, SupportedDataStreamTransportConfiguration: 174, SupportedDiagnosticsSnapshot: 175, SupportedRTPConfiguration: 176, SupportedRouterConfiguration: 177, SupportedTransferTransportConfiguration: 178, SupportedVideoRecordingConfiguration: 179, SupportedVideoStreamConfiguration: 180, SwingMode: 181, TargetAirPurifierState: 182, TargetAirQuality: 183, TargetControlList: 184, TargetControlSupportedConfiguration: 185, TargetDoorState: 186, TargetFanState: 187, TargetHeaterCoolerState: 188, TargetHeatingCoolingState: 189, TargetHorizontalTiltAngle: 190, TargetHumidifierDehumidifierState: 191, TargetMediaState: 192, TargetPosition: 193, TargetRelativeHumidity: 194, TargetSlatState: 195, TargetTemperature: 196, TargetTiltAngle: 197, TargetVerticalTiltAngle: 198, TargetVisibilityState: 199, TemperatureDisplayUnits: 200, ThirdPartyCameraActive: 201, TimeUpdate: 202, TransmitPower: 203, MaximumTransmitPower: 204, TunnelConnectionTimeout: 205, TunneledAccessoryAdvertising: 206, TunneledAccessoryConnected: 207, TunneledAccessoryStateNumber: 208, VOCDensity: 209, ValveType: 210, Version: 211, VideoAnalysisActive: 212, Volume: 213, VolumeControlType: 214, VolumeSelector: 215, WANConfigurationList: 216, WANStatusList: 217, WakeConfiguration: 218, WaterLevel: 219, WiFiCapabilities: 220, WiFiConfigurationControl: 221, WiFiSatelliteStatus: 222, AssetUpdateReadiness: 223, SupportedAssetTypes: 224, ConfigurationState: 225, NFCAccessControlPoint: 226, NFCAccessSupportedConfiguration: 227, SiriEndpointSessionStatus: 228, ThreadControlPoint: 229, ThreadNodeCapabilities: 230, ThreadStatus: 231, ThreadOpenThreadVersion: 232, // Added Mar 2024 AccessCodeControlPoint: 233, AccessCodeSupportedConfiguration: 234, AirPlayEnable: 235, CharacteristicValueActiveTransitionCount: 236, CryptoHash: 237, FirmwareUpdateReadiness: 238, FirmwareUpdateStatus: 239, HardwareFinish: 240, MetricsBufferFullState: 241, MultifunctionButton: 242, SelectedDiagnosticsModes: 243, SelectedSleepConfiguration: 244, SiriEnable: 245, SiriEngineVersion: 246, SiriLightOnUse: 247, SiriListening: 248, SiriTouchToUse: 249, StagedFirmwareVersion: 250, SupportedMetrics: 251, SupportedFirmwareUpdateConfiguration: 252, TapType: 253, Token: 254, EOL: 255, properties: { }, accEnumIndexToUC: function( index ) { return CMD4_ACC_TYPE_ENUM.properties[ index ].type; }, accEnumIndexToLC: function( index ) { return CMD4_ACC_TYPE_ENUM.properties[ index ].sche; }, indexOfEnum: function( characteristicString ) { let accTypeEnumIndex = -1; accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === characteristicString ); if ( accTypeEnumIndex >= 0 ) return accTypeEnumIndex; return CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.sche === characteristicString ); }, add: function( api, type, description, props, validValues ) { let UUID = api.hap.uuid.generate( type ); let characteristic = new CustomCharacteristic( api, type, UUID, props ); let hapFormats = api.hap.Formats; let stringConversionFunction; switch ( props.format ) { case hapFormats.BOOL: stringConversionFunction = function ( str ){ return stringToBool( str ); }; props.allowedWordCount = 1; break; case hapFormats.INT: case hapFormats.UINT8: case hapFormats.UINT16: case hapFormats.UINT32: case hapFormats.UINT64: stringConversionFunction = function ( str ){ return stringToInt( str ); }; props.allowedWordCount = 1; break; case hapFormats.FLOAT: stringConversionFunction = function ( str ){ return stringToFloat( str ); }; props.allowedWordCount = 1; break; case hapFormats.STRING: stringConversionFunction = function ( str ){ return stringToString( str ); }; props.allowedWordCount = 0; break; case hapFormats.DATA: stringConversionFunction = function ( str ){ return stringPassThru( str ); }; props.allowedWordCount = 0; break; case hapFormats.TLV8: stringConversionFunction = function ( str ){ return stringToTLV8( str ); }; props.allowedWordCount = 0; break; case hapFormats.DICT: stringConversionFunction = function ( str ){ return stringPassThru( str ); }; props.allowedWordCount = 0; break; } CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.EOL ] = { "type": type, "characteristic": characteristic.characteristic[ type ], "UUID": UUID, "description": description, "relatedCurrentAccTypeEnumIndex": null, "relatedTargetAccTypeEnumIndex": null, "stringConversionFunction": stringConversionFunction, "props": props, "validValues": validValues }; CMD4_ACC_TYPE_ENUM[ type ] = CMD4_ACC_TYPE_ENUM.EOL; CMD4_ACC_TYPE_ENUM.EOL ++; // The returned characteristic is not good for anything but unit testing. // The one used for services is the above: characteristic.characteristic[ type ] return characteristic; }, }; // Export both the init function and the uninitialized data for unit testing module.exports = { init: function (Characteristic, hapFormats, hapUnits, hapPerms) { // Fill in the properties of all possible characteristics // props was added because calling getCharacteridtic().props.perms adds // the characteristic in by default. This costs some lines, but is advantageous. // NOTE - DO NOT CHANGE TYPE TO LOWER CASE. // - HOW Would you handle MACTransmission, UUID, ... // If checking char.At(0).toUpperCase ? // :g/type: "\([A-Z]\)/s//type: "\l\1/g // :g/sche: "\([A-Z]\)/s//sche: "\l\1/g // :g/type: "\([a-z]\)/s//type: "\u\1/g CMD4_ACC_TYPE_ENUM.properties = { 0: { type: "AccessControlLevel", sche: "accessControlLevel", characteristic: Characteristic.AccessControlLevel, deprecated: false, UUID: "000000E5-0000-1000-8000-0026BB765291", description: "The Devices Access Control Level", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT16, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 1: { type: "AccessoryFlags", sche: "accessoryFlags", characteristic: Characteristic.AccessoryFlags, deprecated: false, UUID: "000000A6-0000-1000-8000-0026BB765291", description: "The Devices Accessory Flags", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 2: { type: "AccessoryIdentifier", // HomeKitTypes-Bridge sche: "accessoryIdentifier", // HomeKitTypes-Bridge characteristic: Characteristic.AccessoryIdentifier, deprecated: false, UUID: "00000057-0000-1000-8000-0026BB765291", description: "The Devices Accessory Identifier String", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 3: { type: "Active", sche: "active", characteristic: Characteristic.Active, deprecated: false, UUID: "000000B0-0000-1000-8000-0026BB765291", description: "If the Device is ACTIVE/INACTIVE", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.Active, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.Active, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 {"INACTIVE": Characteristic.Active.INACTIVE, "ACTIVE": Characteristic.Active.ACTIVE } }, 4: { type: "ActiveIdentifier", // HomeKitTypes-Television sche: "activeIdentifier", // HomeKitTypes-Television characteristic: Characteristic.ActiveIdentifier, deprecated: false, UUID: "000000E7-0000-1000-8000-0026BB765291", description: "The Devices Active Identifier", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 5: { type: "ActivityInterval", // HomeKit - Checke3d 11-19-2020 sche: "activityInterval", // HomeKit - Checke3d 11-19-2020 characteristic: Characteristic.ActivityInterval, deprecated: false, UUID: "0000023B-0000-1000-8000-0026BB765291", description: "The Devices Activity Interval", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 6: { type: "AdministratorOnlyAccess", sche: "administratorOnlyAccess", characteristic: Characteristic.AdministratorOnlyAccess, deprecated: false, UUID: "00000001-0000-1000-8000-0026BB765291", description: "If the Device has 2cwAdmin Only Access", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 7: { type: "AirParticulateDensity", sche: "airParticulateDensity", characteristic: Characteristic.AirParticulateDensity, deprecated: false, UUID: "00000064-0000-1000-8000-0026BB765291", description: "The Measured Air Particulate Density", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 1000, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 8: { type: "AirParticulateSize", sche: "airParticulateSize", characteristic: Characteristic.AirParticulateSize, deprecated: false, UUID: "00000065-0000-1000-8000-0026BB765291", description: "The Measured Air Particulate Size", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 {"_2_5_M": Characteristic.AirParticulateSize._2_5_M, "_10_M": Characteristic.AirParticulateSize._10_M } }, 9: { type: "AirQuality", sche: "airQuality", characteristic: Characteristic.AirQuality, deprecated: false, UUID: "00000095-0000-1000-8000-0026BB765291", description: "The Current Air Quality", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, // @deprecated Removed and not used anymore as of homebridge v2 // CMD4_ACC_TYPE_ENUM.TargetAirQuality, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 5, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 {"UNKNOWN": Characteristic.AirQuality.UNKNOWN, "EXCELLENT": Characteristic.AirQuality.EXCELLENT, "GOOD": Characteristic.AirQuality.GOOD, "FAIR": Characteristic.AirQuality.FAIR, "INFERIOR": Characteristic.AirQuality.INFERIOR, "POOR": Characteristic.AirQuality.POOR } }, 10: { type: "AppMatchingIdentifier", sche: "appMatchingIdentifier", characteristic: Characteristic.AppMatchingIdentifier, deprecated: false, UUID: "000000A4-0000-1000-8000-0026BB765291", description: "The Devices App Matching Identifier", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, /* eslint no-unused-vars: ["error", {"args": "none"}] */ stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: // Checked 11/20/2020 {} }, 11: { type: "AudioFeedback", sche: "audioFeedback", characteristic: Characteristic.AudioFeedback, deprecated: false, UUID: "00000005-0000-1000-8000-0026BB765291", description: "If Audio Feedback is Present", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 12: { type: "BatteryLevel", sche: "batteryLevel", characteristic: Characteristic.BatteryLevel, deprecated: false, UUID: "00000068-0000-1000-8000-0026BB765291", description: "The Measured Battery Level", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 13: { type: "Brightness", sche: "brightness", characteristic: Characteristic.Brightness, deprecated: false, UUID: "00000008-0000-1000-8000-0026BB765291", description: "The Percentage of Existing Brightness", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 14: { type: "ButtonEvent", sche: "buttonEvent", characteristic: Characteristic.ButtonEvent, deprecated: false, UUID: "00000126-0000-1000-8000-0026BB765291", description: "The Devices zbutton Event", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 15: { type: "CCAEnergyDetectThreshold", sche: "ccaEnergyDetectThreshold", characteristic: Characteristic.CCAEnergyDetectThreshold, deprecated: false, UUID: "00000246-0000-1000-8000-0026BB765291", description: "The Devices CCA Energy Detect Threshold", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 16: { type: "CCASignalDetectThreshold", sche: "ccaSignalDetectThreshold", characteristic: Characteristic.CCASignalDetectThreshold, deprecated: false, UUID: "00000245-0000-1000-8000-0026BB765291", description: "The Devices Signal Detect Threshold", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 17: { type: "CameraOperatingModeIndicator", sche: "cameraOperatingModeIndicator", characteristic: Characteristic.CameraOperatingModeIndicator, deprecated: false, UUID: "0000021D-0000-1000-8000-0026BB765291", description: "The State of the Camera's Operating Mode Indicator", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY, hapPerms.TIMED_WRITE ] }, validValues: { "DISABLE": 0, "ENABLE": 1 } }, 18: { type: "CarbonDioxideDetected", sche: "carbonDioxideDetected", characteristic: Characteristic.CarbonDioxideDetected, deprecated: false, UUID: "00000092-0000-1000-8000-0026BB765291", description: "If Carbon Dioxide has been Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"CO2_LEVELS_NORMAL": Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL, "CO2_LEVELS_ABNORMAL": Characteristic.CarbonDioxideDetected.CO2_LEVELS_ABNORMAL } }, 19: { type: "CarbonDioxideLevel", sche: "carbonDioxideLevel", characteristic: Characteristic.CarbonDioxideLevel, deprecated: false, UUID: "00000093-0000-1000-8000-0026BB765291", description: "The Amount of CO2 Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 100000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 20: { type: "CarbonDioxidePeakLevel", sche: "carbonDioxidePeakLevel", characteristic: Characteristic.CarbonDioxidePeakLevel, deprecated: false, UUID: "00000094-0000-1000-8000-0026BB765291", description: "The Maximum Amount of CO2 Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 100000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 21: { type: "CarbonMonoxideDetected", sche: "carbonMonoxideDetected", characteristic: Characteristic.CarbonMonoxideDetected, deprecated: false, UUID: "00000069-0000-1000-8000-0026BB765291", description: "Indication of CO Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"CO_LEVELS_NORMAL": Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL, "CO_LEVELS_ABNORMAL": Characteristic.CarbonMonoxideDetected.CO_LEVELS_ABNORMAL } }, 22: { type: "CarbonMonoxideLevel", sche: "carbonMonoxideLevel", characteristic: Characteristic.CarbonMonoxideLevel, deprecated: false, UUID: "00000090-0000-1000-8000-0026BB765291", description: "The Amount of CO Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 23: { type: "CarbonMonoxidePeakLevel", sche: "carbonMonoxidePeakLevel", characteristic: Characteristic.CarbonMonoxidePeakLevel, deprecated: false, UUID: "00000091-0000-1000-8000-0026BB765291", description: "The Maximum Amount of CO Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 24: { type: "Category", // HomeKitTypes-Bridge sche: "category", // HomeKitTypes-Bridge characteristic: Characteristic.Category, deprecated: true, UUID: "000000A3-0000-1000-8000-0026BB765291", description: "A Hint to HomeKitf of which Icon to Use", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT16, allowedWordCount: 1, maxValue: 16, minValue: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 25: { type: "CharacteristicValueTransitionControl", // HomeKit sche: "characteristicValueTransitionControl", // HomeKit characteristic: Characteristic.CharacteristicValueTransitionControl, deprecated: false, UUID: "00000143-0000-1000-8000-0026BB765291", description: "The Devices Characteristic Value Transition Control", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 26: { type: "ChargingState", sche: "chargingState", characteristic: Characteristic.ChargingState, deprecated: false, UUID: "0000008F-0000-1000-8000-0026BB765291", description: "The Current Charging State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020. Note: HomeKit Spec has inProgress, Not CHARGING {"NOT_CHARGING": Characteristic.ChargingState.NOT_CHARGING, "CHARGING": Characteristic.ChargingState.CHARGING, "NOT_CHARGEABLE": Characteristic.ChargingState.NOT_CHARGEABLE } }, 27: { type: "ClosedCaptions", // HomeKitTypes-Television sche: "closedCaptions", // HomeKitTypes-Television characteristic: Characteristic.ClosedCaptions, deprecated: false, UUID: "000000DD-0000-1000-8000-0026BB765291", description: "If Closed Captioning is Enabled", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"DISABLED": Characteristic.ClosedCaptions.DISABLED, "ENABLED": Characteristic.ClosedCaptions.ENABLED } }, 28: { type: "ColorTemperature", sche: "colorTemperature", characteristic: Characteristic.ColorTemperature, deprecated: false, UUID: "000000CE-0000-1000-8000-0026BB765291", description: "The Colors Current Temperature", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, maxValue: 500, minValue: 140, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 29: { type: "ConfigureBridgedAccessory", // HomeKitTypes-Bridge sche: "configureBridgedAccessory", // HomeKitTypes-Bridge characteristic: Characteristic.ConfigureBridgedAccessory, deprecated: true, UUID: "000000A0-0000-1000-8000-0026BB765291", description: "The Configured Bridge Accessory", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.WRITE ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 30: { type: "ConfigureBridgedAccessoryStatus", // HomeKitTypes-Bridge sche: "configureBridgedAccessoryStatus", // HomeKitTypes-Bridge characteristic: Characteristic.ConfigureBridgedAccessoryStatus, deprecated: true, UUID: "0000009D-0000-1000-8000-0026BB765291", description: "The Configured Bridge Accessory Status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 31: { type: "ConfiguredName", // HomeKitTypes-Television sche: "configuredName", // HomeKitTypes-Television characteristic: Characteristic.ConfiguredName, deprecated: false, UUID: "000000E3-0000-1000-8000-0026BB765291", description: "Name of accessory, as displayed in HomeKit", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 32: { type: "ContactSensorState", sche: "contactSensorState", characteristic: Characteristic.ContactSensorState, deprecated: false, UUID: "0000006A-0000-1000-8000-0026BB765291", description: "The Current RState of a Contact Sensor", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"CONTACT_DETECTED": Characteristic.ContactSensorState.CONTACT_DETECTED, "CONTACT_NOT_DETECTED": Characteristic.ContactSensorState.CONTACT_NOT_DETECTED } }, 33: { type: "CoolingThresholdTemperature", sche: "coolingThresholdTemperature", characteristic: Characteristic.CoolingThresholdTemperature, deprecated: false, UUID: "0000000D-0000-1000-8000-0026BB765291", description: "The Current Cooling Threshold Temperature", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.CELSIUS, maxValue: 35, minValue: 10, minStep: 0.1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 34: { type: "CurrentAirPurifierState", sche: "currentAirPurifierState", characteristic: Characteristic.CurrentAirPurifierState, deprecated: false, UUID: "000000A9-0000-1000-8000-0026BB765291", description: "The Current Target Air Purification State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetAirPurifierState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 {"INACTIVE": Characteristic.CurrentAirPurifierState.INACTIVE, "IDLE": Characteristic.CurrentAirPurifierState.IDLE, "PURIFYING_AIR": Characteristic.CurrentAirPurifierState.PURIFYING_AIR } }, 35: { type: "CurrentAmbientLightLevel", sche: "currentAmbientLightLevel", characteristic: Characteristic.CurrentAmbientLightLevel, deprecated: false, UUID: "0000006B-0000-1000-8000-0026BB765291", description: "The Measured Ambient Light Level", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.LUX, maxValue: 100000, minValue: 0.0001, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 36: { type: "CurrentDoorState", sche: "currentDoorState", characteristic: Characteristic.CurrentDoorState, deprecated: false, UUID: "0000000E-0000-1000-8000-0026BB765291", description: "The Doors Current Operating State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetDoorState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 4, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"OPEN": Characteristic.CurrentDoorState.OPEN, "CLOSED": Characteristic.CurrentDoorState.CLOSED, "OPENING": Characteristic.CurrentDoorState.OPENING, "CLOSING": Characteristic.CurrentDoorState.CLOSING, "STOPPED": Characteristic.CurrentDoorState.STOPPED } }, 37: { type: "CurrentFanState", sche: "currentFanState", characteristic: Characteristic.CurrentFanState, deprecated: false, UUID: "000000AF-0000-1000-8000-0026BB765291", description: "The Fans Current Operating State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetFanState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"INACTIVE": Characteristic.CurrentFanState.INACTIVE, "IDLE": Characteristic.CurrentFanState.IDLE, "BLOWING_AIR": Characteristic.CurrentFanState.BLOWING_AIR } }, 38: { type: "CurrentHeaterCoolerState", sche: "currentHeaterCoolerState", characteristic: Characteristic.CurrentHeaterCoolerState, deprecated: false, UUID: "000000B1-0000-1000-8000-0026BB765291", description: "The Heater/Coolers Current Operating State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetHeaterCoolerState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 {"INACTIVE": Characteristic.CurrentHeaterCoolerState.INACTIVE, "IDLE": Characteristic.CurrentHeaterCoolerState.IDLE, "HEATING": Characteristic.CurrentHeaterCoolerState.HEATING, "COOLING": Characteristic.CurrentHeaterCoolerState.COOLING } }, 39: { type: "CurrentHeatingCoolingState", sche: "currentHeatingCoolingState", characteristic: Characteristic.CurrentHeatingCoolingState, deprecated: false, UUID: "0000000F-0000-1000-8000-0026BB765291", description: "The Current Mode of the Heating/Cooling Device", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetHeatingCoolingState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked May/07/2021 {"OFF": Characteristic.CurrentHeatingCoolingState.OFF, "HEAT": Characteristic.CurrentHeatingCoolingState.HEAT, "COOL": Characteristic.CurrentHeatingCoolingState.COOL, // We fix this in checkCharacteristicNeedsFixing "AUTO": Characteristic.TargetHeatingCoolingState.AUTO } }, 40: { type: "CurrentHorizontalTiltAngle", sche: "currentHorizontalTiltAngle", characteristic: Characteristic.CurrentHorizontalTiltAngle, deprecated: false, UUID: "0000006C-0000-1000-8000-0026BB765291", description: "The Current Horizontal Tilt Angle", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetHorizontalTiltAngle, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 90, minValue: -90, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 41: { type: "CurrentHumidifierDehumidifierState", sche: "currentHumidifierDehumidifierState", characteristic: Characteristic.CurrentHumidifierDehumidifierState, deprecated: false, UUID: "000000B3-0000-1000-8000-0026BB765291", description: "The Humidifier or Dehumidifier Current State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetHumidifierDehumidifierState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"INACTIVE": Characteristic.CurrentHumidifierDehumidifierState.INACTIVE, "IDLE": Characteristic.CurrentHumidifierDehumidifierState.IDLE, "HUMIDIFYING": Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING, "DEHUMIDIFYING": Characteristic.CurrentHumidifierDehumidifierState.DEHUMIDIFYING } }, 42: { type: "CurrentMediaState", // HomKitTypes-Television sche: "currentMediaState", // HomKitTypes-Television characteristic: Characteristic.CurrentMediaState, deprecated: false, UUID: "000000E0-0000-1000-8000-0026BB765291", description: "The Medias Current State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetMediaState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 5, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, // HomeKit forgets to define these validValues: {"PLAY": Characteristic.CurrentMediaState.PLAY, "PAUSE": Characteristic.CurrentMediaState.PAUSE, "STOP": Characteristic.CurrentMediaState.STOP, "LOADING": Characteristic.CurrentMediaState.LOADING, "INTERRUPTED": Characteristic.CurrentMediaState.INTERRUPTED} }, 43: { type: "CurrentPosition", sche: "currentPosition", characteristic: Characteristic.CurrentPosition, deprecated: false, UUID: "0000006D-0000-1000-8000-0026BB765291", description: "The devices Current Position", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetPosition, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 44: { type: "CurrentRelativeHumidity", sche: "currentRelativeHumidity", characteristic: Characteristic.CurrentRelativeHumidity, deprecated: false, UUID: "00000010-0000-1000-8000-0026BB765291", description: "The Measured Current Relative Humidity", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetRelativeHumidity, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 45: { type: "CurrentSlatState", sche: "currentSlatState", characteristic: Characteristic.CurrentSlatState, deprecated: false, UUID: "000000AA-0000-1000-8000-0026BB765291", description: "The Slats Current State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: // @deprecated Removed and not used anymore as of homebridge v2 null, // CMD4_ACC_TYPE_ENUM.TargetSlatState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 Note HomeKit Spec has "Stationary" not "Jammed" {"FIXED": Characteristic.CurrentSlatState.FIXED, "JAMMED": Characteristic.CurrentSlatState.JAMMED, "SWINGING": Characteristic.CurrentSlatState.SWINGING } }, 46: { type: "CurrentTemperature", sche: "currentTemperature", characteristic: Characteristic.CurrentTemperature, deprecated: false, UUID: "00000011-0000-1000-8000-0026BB765291", description: "The Current Measured Temperature", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetTemperature, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, // Checked May 19, 2021 props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.CELSIUS, maxValue: 100, minValue: -270, minStep: 0.1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 47: { type: "CurrentTiltAngle", sche: "currentTiltAngle", characteristic: Characteristic.CurrentTiltAngle, deprecated: false, UUID: "000000C1-0000-1000-8000-0026BB765291", description: "The Current Measured Tilt Angle", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetTiltAngle, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 90, minValue: -90, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 48: { type: "CurrentTime", // HomeKitTypes-Bridge sche: "currentTime", // HomeKitTypes-Bridge characteristic: Characteristic.CurrentTime, deprecated: true, UUID: "0000009B-0000-1000-8000-0026BB765291", description: "What Time it is Now", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 49: { type: "CurrentTransport", // HomeKit.d.ts sche: "currentTransport", // HomeKit.d.ts characteristic: Characteristic.CurrentTransport, deprecated: false, UUID: "0000022B-0000-1000-8000-0026BB765291", description: "The Devices Current Transport", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 50: { type: "CurrentVerticalTiltAngle", sche: "currentVerticalTiltAngle", characteristic: Characteristic.CurrentVerticalTiltAngle, deprecated: false, UUID: "0000006E-0000-1000-8000-0026BB765291", description: "TheMeasured Current Vertical Tilt Angle", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetVerticalTiltAngle, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 90, minValue: -90, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 51: { type: "CurrentVisibilityState", // HomeKitTypes-Television sche: "currentVisibilityState", // HomeKitTypes-Television characteristic: Characteristic.CurrentVisibilityState, deprecated: false, UUID: "00000135-0000-1000-8000-0026BB765291", description: "Is a Device Currently Being Shown or Hidden", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.TargetVisibilityState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"SHOWN": Characteristic.CurrentVisibilityState.SHOWN, "HIDDEN": Characteristic.CurrentVisibilityState.HIDDEN } }, 52: { type: "DataStreamHAPTransport", // HomeKit.d.ts sche: "dataStreamHAPTransport", // HomeKit.d.ts characteristic: Characteristic.DataStreamHAPTransport, deprecated: false, UUID: "00000138-0000-1000-8000-0026BB765291", description: "The Devices Data Stream Transport", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 53: { type: "DataStreamHAPTransportInterrupt", // HomeKit.d.ts sche: "dataStreamHAPTransportInterrupt", // HomeKit.d.ts characteristic: Characteristic.DataStreamHAPTransportInterrupt, deprecated: false, UUID: "00000139-0000-1000-8000-0026BB765291", description: "The Devices Data Stream Transport Interrupt", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 54: { type: "DayoftheWeek", // HomeKitTypes-Bridge sche: "dayoftheWeek", // HomeKitTypes-Bridge characteristic: Characteristic.DayoftheWeek, deprecated: true, UUID: "00000098-0000-1000-8000-0026BB765291", description: "The Current Numerical Day of the Week", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 7, minValue: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 55: { type: "DiagonalFieldOfView", // HomeKit.d.ts sche: "diagonalFieldOfView", // HomeKit.d.ts characteristic: Characteristic.DiagonalFieldOfView, deprecated: false, UUID: "00000224-0000-1000-8000-0026BB765291", description: "The Measured Diagonal Field of View", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 360, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 56: { type: "DigitalZoom", sche: "digitalZoom", characteristic: Characteristic.DigitalZoom, deprecated: false, UUID: "0000011D-0000-1000-8000-0026BB765291", description: "The Measured Digital Zoom", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, minStep: 0.1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 57: { type: "DiscoverBridgedAccessories", // HomKitTypes-Bridge sche: "discoverBridgedAccessories", // HomKitTypes-Bridge characteristic: Characteristic.DiscoverBridgedAccessories, deprecated: true, UUID: "0000009E-0000-1000-8000-0026BB765291", description: "The Discovered Bridge Accessories", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues:{} }, // @deprecated Removed and not used anymore as of homebridge v2 58: { type: "DiscoveredBridgedAccessories", // HomKitTypes-Bridge sche: "discoveredBridgedAccessories", // HomKitTypes-Bridge characteristic: Characteristic.DiscoveredBridgedAccessories, deprecated: true, UUID: "0000009F-0000-1000-8000-0026BB765291", description: "The Discovered Bridged Accessories", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT16, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 59: { type: "DisplayOrder", // HomKitTypes-Television sche: "displayOrder", // HomKitTypes-Television characteristic: Characteristic.DisplayOrder, deprecated: false, UUID: "00000136-0000-1000-8000-0026BB765291", description: "The Display Order", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 60: { type: "EventRetransmissionMaximum", // HomeKit.d.ts sche: "eventRetransmissionMaximum", // HomeKit.d.ts characteristic: Characteristic.EventRetransmissionMaximum, deprecated: false, UUID: "0000023D-0000-1000-8000-0026BB765291", description: "The Event Retransmission Maximum Amount", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 61: { type: "EventSnapshotsActive", // HomeKit.d.ts sche: "eventSnapshotsActive", // HomeKit.d.ts characteristic: Characteristic.EventSnapshotsActive, deprecated: false, UUID: "00000223-0000-1000-8000-0026BB765291", description: "Is the Event Snapshot Enabled", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"DISABLE": Characteristic.EventSnapshotsActive.DISABLE, "ENABLE": Characteristic.EventSnapshotsActive.ENABLE, } }, 62: { type: "EventTransmissionCounters", // HomeKit.d.ts sche: "eventTransmissionCounters", // HomeKit.d.ts characteristic: Characteristic.EventTransmissionCounters, deprecated: false, UUID: "0000023E-0000-1000-8000-0026BB765291", description: "The Event Transmission Counters", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 63: { type: "FilterChangeIndication", sche: "filterChangeIndication", characteristic: Characteristic.FilterChangeIndication, deprecated: false, UUID: "000000AC-0000-1000-8000-0026BB765291", description: "An Filters Current Quality", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020. HomeKit Spec has these as "notNeeded" and "needed" {"FILTER_OK": Characteristic.FilterChangeIndication.FILTER_OK, "CHANGE_FILTER": Characteristic.FilterChangeIndication.CHANGE_FILTER } }, 64: { type: "FilterLifeLevel", sche: "filterLifeLevel", characteristic: Characteristic.FilterLifeLevel, deprecated: false, UUID: "000000AB-0000-1000-8000-0026BB765291", description: "An Measurement of Filters Current Quality", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 65: { type: "FirmwareRevision", sche: "firmwareRevision", characteristic: Characteristic.FirmwareRevision, deprecated: false, UUID: "00000052-0000-1000-8000-0026BB765291", description: "The Firmwares Revision String", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 66: { type: "HardwareRevision", sche: "hardwareRevision", characteristic: Characteristic.HardwareRevision, deprecated: false, UUID: "00000053-0000-1000-8000-0026BB765291", description: "The Hardwares Revision String", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 67: { type: "HeartBeat", sche: "heartBeat", characteristic: Characteristic.HeartBeat, deprecated: false, UUID: "0000024A-0000-1000-8000-0026BB765291", description: "The Current Heart Rate", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ ] }, validValues: {} }, 68: { type: "HeatingThresholdTemperature", sche: "heatingThresholdTemperature", characteristic: Characteristic.HeatingThresholdTemperature, deprecated: false, UUID: "00000012-0000-1000-8000-0026BB765291", description: "The Heating Threshold Temperature", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.CELSIUS, maxValue: 25, minValue: 0, minStep: 0.1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 69: { type: "HoldPosition", sche: "holdPosition", characteristic: Characteristic.HoldPosition, deprecated: false, UUID: "0000006F-0000-1000-8000-0026BB765291", description: "If Position Should Be Held", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_WRITE ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 70: { type: "HomeKitCameraActive", sche: "homeKitCameraActive", characteristic: Characteristic.HomeKitCameraActive, deprecated: false, UUID: "0000021B-0000-1000-8000-0026BB765291", description: "Is the HomeKit Camera ON/OFF", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: { "OFF": 0, "ON": 1 } }, 71: { type: "Hue", sche: "hue", characteristic: Characteristic.Hue, deprecated: false, UUID: "00000013-0000-1000-8000-0026BB765291", description: "The Measured Hue", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 360, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 72: { type: "Identifier", // HomeKitTypes-Television sche: "identifier", // HomeKitTypes-Television characteristic: Characteristic.Identifier, deprecated: false, UUID: "000000E6-0000-1000-8000-0026BB765291", description: "The Devices Identifier", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 73: { type: "Identify", sche: "identify", characteristic: Characteristic.Identify, deprecated: false, UUID: "00000014-0000-1000-8000-0026BB765291", description: "The Devices Identify Status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_WRITE ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 74: { type: "ImageMirroring", sche: "imageMirroring", characteristic: Characteristic.ImageMirroring, deprecated: false, UUID: "0000011F-0000-1000-8000-0026BB765291", description: "Is Image Being Mirrored", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 75: { type: "ImageRotation", sche: "imageRotation", characteristic: Characteristic.ImageRotation, deprecated: false, UUID: "0000011E-0000-1000-8000-0026BB765291", description: "The Images Degree of Rotation ", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 360, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 76: { type: "InUse", sche: "inUse", characteristic: Characteristic.InUse, deprecated: false, UUID: "000000D2-0000-1000-8000-0026BB765291", description: "Is the Device In Use", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"NOT_IN_USE": Characteristic.InUse.NOT_IN_USE, "IN_USE": Characteristic.InUse.IN_USE } }, 77: { type: "InputDeviceType", // HomeKitTypes-Television sche: "inputDeviceType", // HomeKitTypes-Television characteristic: Characteristic.InputDeviceType, deprecated: false, UUID: "000000DC-0000-1000-8000-0026BB765291", description: "The Input Devices Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 6, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"OTHER": Characteristic.InputDeviceType.OTHER, "TV": Characteristic.InputDeviceType.TV, "RECORDING": Characteristic.InputDeviceType.RECORDING, "TUNER": Characteristic.InputDeviceType.TUNER, "PLAYBACK": Characteristic.InputDeviceType.PLAYBACK, "AUDIO_SYSTEM": Characteristic.InputDeviceType.AUDIO_SYSTEM, "UNKNOWN_6": Characteristic.InputDeviceType.UNKNOWN_6 } }, 78: { type: "InputSourceType", // HomeKitTypes-Television sche: "inputSourceType", // HomeKitTypes-Television characteristic: Characteristic.InputSourceType, deprecated: false, UUID: "000000DB-0000-1000-8000-0026BB765291", description: "The Input Devices Source Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 10, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"OTHER": Characteristic.InputSourceType.OTHER, "HOME_SCREEN": Characteristic.InputSourceType.HOME_SCREEN, "TUNER": Characteristic.InputSourceType.TUNER, "HDMI": Characteristic.InputSourceType.HDMI, "COMPOSITE_VIDEO": Characteristic.InputSourceType.COMPOSITE_VIDEO, "S_VIDEO": Characteristic.InputSourceType.S_VIDEO, "COMPONENT_VIDEO": Characteristic.InputSourceType.COMPONENT_VIDEO, "DVI": Characteristic.InputSourceType.DVI, "AIRPLAY": Characteristic.InputSourceType.AIRPLAY, "USB": Characteristic.InputSourceType.USB, "APPLICATION": Characteristic.InputSourceType.APPLICATION } }, 79: { type: "IsConfigured", sche: "isConfigured", characteristic: Characteristic.IsConfigured, deprecated: false, UUID: "000000D6-0000-1000-8000-0026BB765291", description: "Is the device Configured", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 {"NOT_CONFIGURED": Characteristic.IsConfigured.NOT_CONFIGURED, "CONFIGURED": Characteristic.IsConfigured.CONFIGURED } }, 80: { type: "LeakDetected", sche: "leakDetected", characteristic: Characteristic.LeakDetected, deprecated: false, UUID: "00000070-0000-1000-8000-0026BB765291", description: "Is There a Leak Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"LEAK_NOT_DETECTED": Characteristic.LeakDetected.LEAK_NOT_DETECTED, "LEAK_DETECTED": Characteristic.LeakDetected.LEAK_DETECTED } }, // @deprecated Removed and not used anymore as of homebridge v2 81: { type: "LinkQuality", // HomeKitTypes-Bridge sche: "linkQuality", // HomeKitTypes-Bridge characteristic: Characteristic.LinkQuality, deprecated: true, UUID: "0000009C-0000-1000-8000-0026BB765291", description: "The Links Quality", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 4, minValue: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 82: { type: "ListPairings", // HomeKitTypes-Bridge sche: "listPairings", // HomeKitTypes-Bridge characteristic: Characteristic.ListPairings, deprecated: false, UUID: "00000050-0000-1000-8000-0026BB765291", description: "The Links Quality", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 83: { type: "LockControlPoint", sche: "lockControlPoint", characteristic: Characteristic.LockControlPoint, deprecated: false, UUID: "00000019-0000-1000-8000-0026BB765291", description: "The Control Point of the Lock", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_WRITE ] }, validValues: {} }, 84: { type: "LockCurrentState", sche: "lockCurrentState", characteristic: Characteristic.LockCurrentState, deprecated: false, UUID: "0000001D-0000-1000-8000-0026BB765291", description: "The Locks Current State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.LockTargetState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"UNSECURED": Characteristic.LockCurrentState.UNSECURED, "SECURED": Characteristic.LockCurrentState.SECURED, "JAMMED": Characteristic.LockCurrentState.JAMMED, "UNKNOWN": Characteristic.LockCurrentState.UNKNOWN } }, 85: { type: "LockLastKnownAction", sche: "lockLastKnownAction", characteristic: Characteristic.LockLastKnownAction, deprecated: false, UUID: "0000001C-0000-1000-8000-0026BB765291", description: "The Locks Last known Action", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 10, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"SECURED_PHYSICALLY_INTERIOR": Characteristic.LockLastKnownAction.SECURED_PHYSICALLY_INTERIOR, "UNSECURED_PHYSICALLY_INTERIOR": Characteristic.LockLastKnownAction.UNSECURED_PHYSICALLY_INTERIOR, "SECURED_PHYSICALLY_EXTERIOR": Characteristic.LockLastKnownAction.SECURED_PHYSICALLY_EXTERIOR, "UNSECURED_PHYSICALLY_EXTERIOR": Characteristic.LockLastKnownAction.UNSECURED_PHYSICALLY_EXTERIOR, "SECURED_BY_KEYPAD": Characteristic.LockLastKnownAction.SECURED_BY_KEYPAD, "UNSECURED_BY_KEYPAD": Characteristic.LockLastKnownAction.UNSECURED_BY_KEYPAD, "SECURED_REMOTELY": Characteristic.LockLastKnownAction.SECURED_REMOTELY, "UNSECURED_REMOTELY": Characteristic.LockLastKnownAction.UNSECURED_REMOTELY, "SECURED_BY_AUTO_SECURE_TIMEOUT": Characteristic.LockLastKnownAction.SECURED_BY_AUTO_SECURE_TIMEOUT, "SECURED_PHYSICALLY": Characteristic.LockLastKnownAction.SECURED_PHYSICALLY, "UNSECURED_PHYSICALLY": Characteristic.LockLastKnownAction.UNSECURED_PHYSICALLY } }, 86: { type: "LockManagementAutoSecurityTimeout", sche: "lockManagementAutoSecurityTimeout", characteristic: Characteristic.LockManagementAutoSecurityTimeout, deprecated: false, UUID: "0000001A-0000-1000-8000-0026BB765291", description: "The Locks Security Timeout Value", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, unit: hapUnits.SECONDS, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 87: { type: "LockPhysicalControls", sche: "lockPhysicalControls", characteristic: Characteristic.LockPhysicalControls, deprecated: false, UUID: "000000A7-0000-1000-8000-0026BB765291", description: "Is the Lock Physically Enabled/Disabled", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"CONTROL_LOCK_DISABLED": Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED, "CONTROL_LOCK_ENABLED": Characteristic.LockPhysicalControls.CONTROL_LOCK_ENABLED } }, 88: { type: "LockTargetState", sche: "lockTargetState", characteristic: Characteristic.LockTargetState, deprecated: false, UUID: "0000001E-0000-1000-8000-0026BB765291", description: "The Locks Requested State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.LockCurrentState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"UNSECURED": Characteristic.LockTargetState.UNSECURED, "SECURED": Characteristic.LockTargetState.SECURED } }, 89: { type: "Logs", sche: "logs", characteristic: Characteristic.Logs, deprecated: false, UUID: "0000001F-0000-1000-8000-0026BB765291", description: "The Devices Logs", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 90: { type: "MACRetransmissionMaximum", // HomekitTypes.d.ts sche: "macRetransmissionMaximum", // HomekitTypes.d.ts characteristic: Characteristic.MACRetransmissionMaximum, deprecated: false, UUID: "00000247-0000-1000-8000-0026BB765291", description: "The Devices MAC Retransmission Maximum Amount", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 91: { type: "MACTransmissionCounters", // HomekitTypes.d.ts sche: "macTransmissionCounters", // HomekitTypes.d.ts characteristic: Characteristic.MACTransmissionCounters, deprecated: false, UUID: "00000248-0000-1000-8000-0026BB765291", description: "The Devices MAC Transmission Counters", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringPassThru( str ); }, props: {format: hapFormats.DATA, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 92: { type: "ManagedNetworkEnable", // HomeKit sche: "managedNetworkEnable", // HomeKit characteristic: Characteristic.ManagedNetworkEnable, deprecated: false, UUID: "00000215-0000-1000-8000-0026BB765291", description: "The Networks Current State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY, hapPerms.TIMED_WRITE ] }, validValues: { "DISABLED": Characteristic.ManagedNetworkEnable.DISABLED, "ENABLED": Characteristic.ManagedNetworkEnable.ENABLED, "UNKNOWN": 2 } }, 93: { type: "ManuallyDisabled", sche: "manuallyDisabled", characteristic: Characteristic.ManuallyDisabled, deprecated: false, UUID: "00000227-0000-1000-8000-0026BB765291", description: "Is the Device Manually Enabled/Disabled", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: { "ENABLED": Characteristic.ManuallyDisabled.ENABLED, "DISABLED": Characteristic.ManuallyDisabled.DISABLED } }, 94: { type: "Manufacturer", sche: "manufacturer", characteristic: Characteristic.Manufacturer, deprecated: false, UUID: "00000020-0000-1000-8000-0026BB765291", description: "The Devices Manufacturer", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 95: { type: "Model", sche: "model", characteristic: Characteristic.Model, deprecated: false, UUID: "00000021-0000-1000-8000-0026BB765291", description: "The Devices Model", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 96: { type: "MotionDetected", sche: "motionDetected", characteristic: Characteristic.MotionDetected, deprecated: false, UUID: "00000022-0000-1000-8000-0026BB765291", description: "Is Motion Being Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 97: { type: "Mute", sche: "mute", characteristic: Characteristic.Mute, deprecated: false, UUID: "0000011A-0000-1000-8000-0026BB765291", description: "Is the Device Currently Muted", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 98: { type: "Name", sche: "name", characteristic: Characteristic.Name, deprecated: false, UUID: "00000023-0000-1000-8000-0026BB765291", description: "Name of accessory, as displayed in HomeKit", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 99: { type: "NetworkAccessViolationControl", // HomeKitTypes.d.ts sche: "networkAccessViolationControl", // HomeKitTypes.d.ts characteristic: Characteristic.NetworkAccessViolationControl, deprecated: false, UUID: "0000021F-0000-1000-8000-0026BB765291", description: "The Network AccessViolation Control", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY, hapPerms.TIMED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 100: { type: "NetworkClientProfileControl", // HomeKitTypes.d.ts sche: "networkClientProfileControl", // HomeKitTypes.d.ts characteristic: Characteristic.NetworkClientProfileControl, deprecated: false, UUID: "0000020C-0000-1000-8000-0026BB765291", description: "The Network Client Profile Control", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY, hapPerms.TIMED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 101: { type: "NetworkClientStatusControl", // HomeKitTypes.d.ts sche: "networkClientStatusControl", // HomeKitTypes.d.ts characteristic: Characteristic.NetworkClientStatusControl, deprecated: false, UUID: "0000020D-0000-1000-8000-0026BB765291", description: "The Network Client Status Control", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 102: { type: "NightVision", sche: "nightVision", characteristic: Characteristic.NightVision, deprecated: false, UUID: "0000011B-0000-1000-8000-0026BB765291", description: "Is Night Vision Available", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY, hapPerms.TIMED_WRITE ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 103: { type: "NitrogenDioxideDensity", sche: "nitrogenDioxideDensity", characteristic: Characteristic.NitrogenDioxideDensity, deprecated: false, UUID: "000000C4-0000-1000-8000-0026BB765291", description: "The Measured NO2 Density", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 1000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 104: { type: "ObstructionDetected", sche: "obstructionDetected", characteristic: Characteristic.ObstructionDetected, deprecated: false, UUID: "00000024-0000-1000-8000-0026BB765291", description: "Is Obstruction Currently Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 105: { type: "OccupancyDetected", sche: "occupancyDetected", characteristic: Characteristic.OccupancyDetected, deprecated: false, UUID: "00000071-0000-1000-8000-0026BB765291", description: "Is Occupancy Currently Detected", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"OCCUPANCY_NOT_DETECTED": Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED, "OCCUPANCY_DETECTED": Characteristic.OccupancyDetected.OCCUPANCY_DETECTED } }, 106: { type: "On", sche: "on", characteristic: Characteristic.On, deprecated: false, UUID: "00000025-0000-1000-8000-0026BB765291", description: "Is the Device On", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 107: { type: "OperatingStateResponse", // HomeKitTypes.d.ts sche: "operatingStateResponse", // HomeKitTypes.d.ts characteristic: Characteristic.OperatingStateResponse, deprecated: false, UUID: "00000232-0000-1000-8000-0026BB765291", description: "The Devices Operating State Response", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 108: { type: "OpticalZoom", sche: "opticalZoom", characteristic: Characteristic.OpticalZoom, deprecated: false, UUID: "0000011C-0000-1000-8000-0026BB765291", description: "The Devices Optical Zoom Factor", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, minStep: .1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 109: { type: "OutletInUse", sche: "outletInUse", characteristic: Characteristic.OutletInUse, deprecated: false, UUID: "00000026-0000-1000-8000-0026BB765291", description: "Is the Outlet in Use", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 110: { type: "OzoneDensity", sche: "ozoneDensity", characteristic: Characteristic.OzoneDensity, deprecated: false, UUID: "000000C3-0000-1000-8000-0026BB765291", description: "The Ozones Current Measured Density", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 1000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 111: { type: "PM10Density", sche: "pm10Density", characteristic: Characteristic.PM10Density, deprecated: false, UUID: "000000C7-0000-1000-8000-0026BB765291", description: "The PM1O Current Measured Density", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 1000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 112: { type: "PM2_5Density", sche: "pm2_5Density", characteristic: Characteristic.PM2_5Density, deprecated: false, UUID: "000000C6-0000-1000-8000-0026BB765291", description: "The PM2_5 Current Measured Density", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 1000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 113: { type: "PairSetup", sche: "pairSetup", characteristic: Characteristic.PairSetup, deprecated: false, UUID: "0000004C-0000-1000-8000-0026BB765291", description: "The Devices Pair Setup", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 114: { type: "PairVerify", sche: "pairVerify", characteristic: Characteristic.PairVerify, deprecated: false, UUID: "0000004E-0000-1000-8000-0026BB765291", description: "The Devices Pair Verify", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 115: { type: "PairingFeatures", sche: "pairingFeatures", characteristic: Characteristic.PairingFeatures, deprecated: false, UUID: "0000004F-0000-1000-8000-0026BB765291", description: "The Devices Pairing Features", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 116: { type: "PairingPairings", sche: "pairingPairings", characteristic: Characteristic.PairingPairings, deprecated: false, UUID: "00000050-0000-1000-8000-0026BB765291", description: "The Devices Pairing Pairings", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 117: { type: "PasswordSetting", // HomeKitTypes.d.ts sche: "passwordSetting", // HomeKitTypes.d.ts characteristic: Characteristic.PasswordSetting, deprecated: false, UUID: "000000E4-0000-1000-8000-0026BB765291", description: "The Devices Password Setting", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 118: { type: "PeriodicSnapshotsActive", // HomeKitTypes.d.ts sche: "periodicSnapshotsActive", // HomeKitTypes.d.ts characteristic: Characteristic.PeriodicSnapshotsActive, deprecated: false, UUID: "00000225-0000-1000-8000-0026BB765291", description: "Is the Periodic Snapshot Enabled/Disabled", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"DISABLE": Characteristic.PeriodicSnapshotsActive.DISABLE, // 0 "ENABLE": Characteristic.PeriodicSnapshotsActive.ENABLE // 1 } }, 119: { type: "PictureMode", // HomeKitTypes-Television sche: "pictureMode", // HomeKitTypes-Television characteristic: Characteristic.PictureMode, deprecated: false, UUID: "000000E2-0000-1000-8000-0026BB765291", description: "The Current Picture Mode", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 13, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"OTHER": Characteristic.PictureMode.OTHER, "STANDARD": Characteristic.PictureMode.STANDARD, "CALIBRATED": Characteristic.PictureMode.CALIBRATED, "CALIBRATED_DARK": Characteristic.PictureMode.CALIBRATED_DARK, "VIVID": Characteristic.PictureMode.VIVID, "GAME": Characteristic.PictureMode.GAME, "COMPUTER": Characteristic.PictureMode.COMPUTER, "CUSTOM": Characteristic.PictureMode.CUSTOM, "UNKNOWN8": 8, "UNKNOWN9": 9, "UNKNOWN10": 10, "UNKNOWN11": 11, "UNKNOWN12": 12, "UNKNOWN13": 13 } }, 120: { type: "Ping", // HomeKitTypes.d.ts sche: "ping", // HomeKitTypes.d.ts characteristic: Characteristic.Ping, deprecated: false, UUID: "0000023C-0000-1000-8000-0026BB765291", description: "The Devices Ping Characteristic", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringPassThru( str ); }, props: {format: hapFormats.DATA, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 121: { type: "PositionState", sche: "positionState", characteristic: Characteristic.PositionState, deprecated: false, UUID: "00000072-0000-1000-8000-0026BB765291", description: "The Devices Position State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"DECREASING": Characteristic.PositionState.DECREASING, "INCREASING": Characteristic.PositionState.INCREASING, "STOPPED": Characteristic.PositionState.STOPPED } }, 122: { type: "PowerModeSelection", // HomeKitTypes-Television sche: "powerModeSelection", // HomeKitTypes-Television characteristic: Characteristic.PowerModeSelection, deprecated: false, UUID: "000000DF-0000-1000-8000-0026BB765291", description: "The Devices Power Mode Selection", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_WRITE ] }, validValues: {"SHOW": Characteristic.PowerModeSelection.SHOW, "HIDE": Characteristic.PowerModeSelection.HIDE } }, 123: { type: "ProductData", // HomeKitTypes.d.ts sche: "productData", // HomeKitTypes.d.ts characteristic: Characteristic.ProductData, deprecated: false, UUID: "00000220-0000-1000-8000-0026BB765291", description: "The Devices Product Data", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringPassThru( str ); }, props: {format: hapFormats.DATA, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 124: { type: "ProgramMode", sche: "programMode", characteristic: Characteristic.ProgramMode, deprecated: false, UUID: "000000D1-0000-1000-8000-0026BB765291", description: "The Devices Program Mode", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"NO_PROGRAM_SCHEDULED": Characteristic.ProgramMode.NO_PROGRAM_SCHEDULED, "PROGRAM_SCHEDULED": Characteristic.ProgramMode.PROGRAM_SCHEDULED, "PROGRAM_SCHEDULED_MANUAL_MODE": Characteristic.ProgramMode.PROGRAM_SCHEDULED_MANUAL_MODE } }, 125: { type: "ProgrammableSwitchEvent", sche: "programmableSwitchEvent", characteristic: Characteristic.ProgrammableSwitchEvent, deprecated: false, UUID: "00000073-0000-1000-8000-0026BB765291", description: "The Devices Current Position State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"SINGLE_PRESS": Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, "DOUBLE_PRESS": Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS, "LONG_PRESS": Characteristic.ProgrammableSwitchEvent.LONG_PRESS } }, 126: { type: "ProgrammableSwitchOutputState", // HomeKitTypes-Bridge sche: "programmableSwitchOutputState", // HomeKitTypes-Bridge characteristic: Characteristic.ProgrammableSwitchOutputState, deprecated: false, UUID: "00000074-0000-1000-8000-0026BB765291", description: "The Programmable Switches Output State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 127: { type: "Reachable", // HomeKitTypes-Bridge sche: "reachable", // HomeKitTypes-Bridge characteristic: Characteristic.Reachable, deprecated: true, UUID: "00000063-0000-1000-8000-0026BB765291", description: "If the Device is Reachable", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 128: { type: "ReceivedSignalStrengthIndication", // HomeKit sche: "receivedSignalStrengthIndication", // HomeKit characteristic: Characteristic.ReceivedSignalStrengthIndication, deprecated: false, UUID: "0000023F-0000-1000-8000-0026BB765291", description: "The Received Signal Strength Measurement", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 129: { type: "ReceiverSensitivity", // HomeKitTypes.d.ts sche: "receiverSensitivity", // HomeKitTypes.d.ts characteristic: Characteristic.ReceiverSensitivity, deprecated: false, UUID: "00000244-0000-1000-8000-0026BB765291", description: "The Amount of Receiver Sensitivity", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 130: { type: "RecordingAudioActive", // HomeKitTypes.d.ts sche: "recordingAudioActive", // HomeKitTypes.d.ts characteristic: Characteristic.RecordingAudioActive, deprecated: false, UUID: "00000226-0000-1000-8000-0026BB765291", description: "Is Recordding Audio Enabled/DisAbled", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY, hapPerms.TIMED_WRITE ] }, validValues: {"DISABLE": Characteristic.RecordingAudioActive.DISABLE, // CHECK "ENABLE": Characteristic.RecordingAudioActive.ENABLE } }, 131: { type: "RelativeHumidityDehumidifierThreshold", sche: "relativeHumidityDehumidifierThreshold", characteristic: Characteristic.RelativeHumidityDehumidifierThreshold, deprecated: false, UUID: "000000C9-0000-1000-8000-0026BB765291", description: "The Relative Humidity DeHumidifier Threshold", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 132: { type: "RelativeHumidityHumidifierThreshold", sche: "relativeHumidityHumidifierThreshold", characteristic: Characteristic.RelativeHumidityHumidifierThreshold, deprecated: false, UUID: "000000CA-0000-1000-8000-0026BB765291", description: "The Relative Humidity Humidifier Threshold", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 133: { type: "RelayControlPoint", // HomeKitTypes-Bridge sche: "relayControlPoint", // HomeKitTypes-Bridge characteristic: Characteristic.RelayControlPoint, deprecated: false, UUID: "0000005E-0000-1000-8000-0026BB765291", description: "The Relay Control Point", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 134: { type: "RelayEnabled", // HomeKitTypes-Bridge sche: "relayEnabled", // HomeKitTypes-Bridge characteristic: Characteristic.RelayEnabled, deprecated: false, UUID: "0000005B-0000-1000-8000-0026BB765291", description: "Is the Relay Currently Enabled", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 135: { type: "RelayState", // HomeKitTypes-Bridge sche: "relayState", // HomeKitTypes-Bridge characteristic: Characteristic.RelayState, deprecated: false, UUID: "0000005C-0000-1000-8000-0026BB765291", description: "The Relays Current State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 5, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 136: { type: "RemainingDuration", sche: "remainingDuration", characteristic: Characteristic.RemainingDuration, deprecated: false, UUID: "000000D4-0000-1000-8000-0026BB765291", description: "The Remaining Duration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, maxValue: 3600, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 137: { type: "RemoteKey", // HomeKitTypes-Television sche: "remoteKey", // HomeKitTypes-Television characteristic: Characteristic.RemoteKey, deprecated: false, UUID: "000000E1-0000-1000-8000-0026BB765291", description: "The Remote Key", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 16, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_WRITE ] }, validValues: {"REWIND": Characteristic.RemoteKey.REWIND, "FAST_FORWARD": Characteristic.RemoteKey.FAST_FORWARD, "NEXT_TRACK": Characteristic.RemoteKey.NEXT_TRACK, "PREVIOUS_TRACK": Characteristic.RemoteKey.PREVIOUS_TRACK, "ARROW_UP": Characteristic.RemoteKey.ARROW_UP, "ARROW_DOWN": Characteristic.RemoteKey.ARROW_DOWN, "ARROW_LEFT": Characteristic.RemoteKey.ARROW_LEFT, "ARROW_RIGHT": Characteristic.RemoteKey.ARROW_RIGHT, "SELECT": Characteristic.RemoteKey.SELECT, "BACK": Characteristic.RemoteKey.BACK, "EXIT": Characteristic.RemoteKey.EXIT, "PLAY_PAUSE": Characteristic.RemoteKey.PLAY_PAUSE, "UNKNOWN12": 12, // HomeBridge does not have these defined for values "UNKNOWN13": 13, // HomeBridge does not have these defined for values "UNKNOWN14": 14, // HomeBridge does not have these defined for values "INFORMATION": Characteristic.RemoteKey.INFORMATION, "UNKNOWN16": 16 // HomeBridge does not have these defined for values } }, 138: { type: "ResetFilterIndication", sche: "resetFilterIndication", characteristic: Characteristic.ResetFilterIndication, deprecated: false, UUID: "000000AD-0000-1000-8000-0026BB765291", description: "If Reset Filter Indication ", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 1, minStep: 1, perms: [hapPerms.PAIRED_WRITE ] }, validValues: {} }, 139: { type: "RotationDirection", sche: "rotationDirection", characteristic: Characteristic.RotationDirection, deprecated: false, UUID: "00000028-0000-1000-8000-0026BB765291", description: "The Current Direction of Rotation", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"CLOCKWISE": Characteristic.RotationDirection.CLOCKWISE, "COUNTER_CLOCKWISE": Characteristic.RotationDirection.COUNTER_CLOCKWISE } }, 140: { type: "RotationSpeed", sche: "rotationSpeed", characteristic: Characteristic.RotationSpeed, deprecated: false, UUID: "00000029-0000-1000-8000-0026BB765291", description: "The Current Speed of Rotation", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 141: { type: "RouterStatus", // HomeKitTypes.d.ts sche: "routerStatus", // HomeKitTypes.d.ts characteristic: Characteristic.RouterStatus, deprecated: false, UUID: "0000020E-0000-1000-8000-0026BB765291", description: "The Current Router Status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"READY" : Characteristic.RouterStatus.READY, // 0 "NOT_READY" : Characteristic.RouterStatus.NOT_READY // 1 } }, 142: { type: "Saturation", sche: "saturation", characteristic: Characteristic.Saturation, deprecated: false, UUID: "0000002F-0000-1000-8000-0026BB765291", description: "The Percentage of Color Saturation", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 143: { type: "SecuritySystemAlarmType", // Checked 03/17/21 sche: "securitySystemAlarmType", // Checked 03/17/21 characteristic: Characteristic.SecuritySystemAlarmType, deprecated: false, UUID: "0000008E-0000-1000-8000-0026BB765291", description: "The Security System Alarm Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 144: { type: "SecuritySystemCurrentState", sche: "securitySystemCurrentState", characteristic: Characteristic.SecuritySystemCurrentState, deprecated: false, UUID: "00000066-0000-1000-8000-0026BB765291", description: "The Security Systems Currently Armed State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.SecuritySystemTargetState, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 4, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"STAY_ARM": Characteristic.SecuritySystemCurrentState.STAY_ARM, "AWAY_ARM": Characteristic.SecuritySystemCurrentState.AWAY_ARM, "NIGHT_ARM": Characteristic.SecuritySystemCurrentState.NIGHT_ARM, "DISARMED": Characteristic.SecuritySystemCurrentState.DISARMED, "ALARM_TRIGGERED": Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED } }, 145: { type: "SecuritySystemTargetState", sche: "securitySystemTargetState", characteristic: Characteristic.SecuritySystemTargetState, deprecated: false, UUID: "00000067-0000-1000-8000-0026BB765291", description: "The Target Armed State of the Security System", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.SecuritySystemCurrentState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"STAY_ARM": Characteristic.SecuritySystemTargetState.STAY_ARM, "AWAY_ARM": Characteristic.SecuritySystemTargetState.AWAY_ARM, "NIGHT_ARM": Characteristic.SecuritySystemTargetState.NIGHT_ARM, "DISARM": Characteristic.SecuritySystemTargetState.DISARM } }, 146: { type: "SelectedAudioStreamConfiguration", // HomeKit-Remote sche: "selectedAudioStreamConfiguration", // HomeKit-Remote characteristic: Characteristic.SelectedAudioStreamConfiguration, deprecated: false, UUID: "00000128-0000-1000-8000-0026BB765291", description: "The Selected Audio Stream Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 147: { type: "SelectedCameraRecordingConfiguration", // HomeKitTypes.d.ts sche: "selectedCameraRecordingConfiguration", // HomeKitTypes.d.ts characteristic: Characteristic.SelectedCameraRecordingConfiguration, deprecated: false, UUID: "00000209-0000-1000-8000-0026BB765291", description: "The Selected Cameras Recording Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 148: { type: "SelectedRTPStreamConfiguration", sche: "selectedRTPStreamConfiguration", characteristic: Characteristic.SelectedRTPStreamConfiguration, deprecated: false, UUID: "00000117-0000-1000-8000-0026BB765291", description: "The Selected RTP Stream Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 149: { type: "SerialNumber", sche: "serialNumber", characteristic: Characteristic.SerialNumber, deprecated: false, UUID: "00000030-0000-1000-8000-0026BB765291", description: "The Devices Serial Number", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 150: { type: "ServiceLabelIndex", sche: "serviceLabelIndex", characteristic: Characteristic.ServiceLabelIndex, deprecated: false, UUID: "000000CB-0000-1000-8000-0026BB765291", description: "The Devices Service Label Index", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 255, minValue: 1, minStep: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 151: { type: "ServiceLabelNamespace", sche: "serviceLabelNamespace", characteristic: Characteristic.ServiceLabelNamespace, deprecated: false, UUID: "000000CD-0000-1000-8000-0026BB765291", description: "The Devices Service Label NameSpace", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {"DOTS": Characteristic.ServiceLabelNamespace.DOTS, "ARABIC_NUMERALS": Characteristic.ServiceLabelNamespace.ARABIC_NUMERALS } }, 152: { type: "SetDuration", sche: "setDuration", characteristic: Characteristic.SetDuration, deprecated: false, UUID: "000000D3-0000-1000-8000-0026BB765291", description: "The Devices Set Duration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, maxValue: 3600, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 153: { type: "SetupDataStreamTransport", // HomeKit-DataStream sche: "setupDataStreamTransport", // HomeKit-DataStream characteristic: Characteristic.SetupDataStreamTransport, deprecated: false, UUID: "00000131-0000-1000-8000-0026BB765291", description: "The Devices Setup Stream Transport Value", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 154: { type: "SetupEndpoints", sche: "setupEndpoints", characteristic: Characteristic.SetupEndpoints, deprecated: false, UUID: "00000118-0000-1000-8000-0026BB765291", description: "The Devices Setup End Points", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 155: { type: "SetupTransferTransport", // HomeKitTypes.d.ts sche: "setupTransferTransport", // HomeKitTypes.d.ts characteristic: Characteristic.SetupTransferTransport, deprecated: false, UUID: "00000201-0000-1000-8000-0026BB765291", description: "The Devices Setup Transfer Transport", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 156: { type: "SignalToNoiseRatio", // HomeKit sche: "signalToNoiseRatio", // HomeKit characteristic: Characteristic.SignalToNoiseRatio, deprecated: false, UUID: "00000241-0000-1000-8000-0026BB765291", description: "The Measured Signal to Noise Ratio", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 157: { type: "SiriInputType", // HomeKit-Remote sche: "siriInputType", // HomeKit-Remote characteristic: Characteristic.SiriInputType, deprecated: false, UUID: "00000132-0000-1000-8000-0026BB765291", description: "siri's Input Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 0, minValue: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: { // Check "PUSH_BUTTON_TRIGGERED_APPLE_TV": Characteristic.SiriInputType.PUSH_BUTTON_TRIGGERED_APPLE_TV } }, 158: { type: "SlatType", sche: "slatType", characteristic: Characteristic.SlatType, deprecated: false, UUID: "000000C0-0000-1000-8000-0026BB765291", description: "The Slat Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {"HORIZONTAL": Characteristic.SlatType.HORIZONTAL, "VERTICAL": Characteristic.SlatType.VERTICAL } }, 159: { type: "SleepDiscoveryMode", // HomeKitTypes-Television sche: "sleepDiscoveryMode", // HomeKitTypes-Television characteristic: Characteristic.SleepDiscoveryMode, deprecated: false, UUID: "000000E8-0000-1000-8000-0026BB765291", description: "The Devices Discoverable Sleep Mode", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"NOT_DISCOVERABLE": Characteristic.SleepDiscoveryMode.NOT_DISCOVERABLE , "ALWAYS_DISCOVERABLE": Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE } }, 160: { type: "SleepInterval", // HomeKitTypes.d.ts sche: "sleepInterval", // HomeKitTypes.d.ts characteristic: Characteristic.SleepInterval, deprecated: false, UUID: "0000023A-0000-1000-8000-0026BB765291", description: "The Devices Sleep Interval", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 161: { type: "SmokeDetected", sche: "smokeDetected", characteristic: Characteristic.SmokeDetected, deprecated: false, UUID: "00000076-0000-1000-8000-0026BB765291", description: "If Smoke is Detected or Not", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"SMOKE_NOT_DETECTED": Characteristic.SmokeDetected.SMOKE_NOT_DETECTED, "SMOKE_DETECTED": Characteristic.SmokeDetected.SMOKE_DETECTED } }, 162: { type: "SoftwareRevision", // HomeKit-Bridge sche: "softwareRevision", // HomeKit-Bridge characteristic: Characteristic.SoftwareRevision, deprecated: false, UUID: "00000054-0000-1000-8000-0026BB765291", description: "The Devices Software Revision String", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 163: { type: "StatusActive", sche: "statusActive", characteristic: Characteristic.StatusActive, deprecated: false, UUID: "00000075-0000-1000-8000-0026BB765291", description: "If the Device is Active or Not", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 164: { type: "StatusFault", sche: "statusFault", characteristic: Characteristic.StatusFault, deprecated: false, UUID: "00000077-0000-1000-8000-0026BB765291", description: "If the Device has a Status Fault", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"NO_FAULT": Characteristic.StatusFault.NO_FAULT, "GENERAL_FAULT": Characteristic.StatusFault.GENERAL_FAULT } }, 165: { type: "StatusJammed", sche: "statusJammed", characteristic: Characteristic.StatusJammed, deprecated: false, UUID: "00000078-0000-1000-8000-0026BB765291", description: "If the Device is in Jammed Status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"NOT_JAMMED": Characteristic.StatusJammed.NOT_JAMMED, "JAMMED": Characteristic.StatusJammed.JAMMED } }, 166: { type: "StatusLowBattery", sche: "statusLowBattery", characteristic: Characteristic.StatusLowBattery, deprecated: false, UUID: "00000079-0000-1000-8000-0026BB765291", description: "The Status of the Battery Level", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"BATTERY_LEVEL_NORMAL": Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, "BATTERY_LEVEL_LOW": Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW } }, 167: { type: "StatusTampered", sche: "statusTampered", characteristic: Characteristic.StatusTampered, deprecated: false, UUID: "0000007A-0000-1000-8000-0026BB765291", description: "If the Device is Tampered", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"NOT_TAMPERED": Characteristic.StatusTampered.NOT_TAMPERED, "TAMPERED": Characteristic.StatusTampered.TAMPERED } }, 168: { type: "StreamingStatus", sche: "streamingStatus", characteristic: Characteristic.StreamingStatus, deprecated: false, UUID: "00000120-0000-1000-8000-0026BB765291", description: "The Devices Streaming Status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 169: { type: "SulphurDioxideDensity", sche: "sulphurDioxideDensity", characteristic: Characteristic.SulphurDioxideDensity, deprecated: false, UUID: "000000C5-0000-1000-8000-0026BB765291", description: "The measured Sulphur Dioxide Density", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 1000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 170: { type: "SupportedAudioRecordingConfiguration", sche: "supportedAudioRecordingConfiguration", characteristic: Characteristic.SupportedAudioRecordingConfiguration, deprecated: false, UUID: "00000207-0000-1000-8000-0026BB765291", description: "The Supported Audio Recording Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 171: { type: "SupportedAudioStreamConfiguration", // HomeKit sche: "supportedAudioStreamConfiguration", // HomeKit characteristic: Characteristic.SupportedAudioStreamConfiguration, deprecated: false, UUID: "00000115-0000-1000-8000-0026BB765291", description: "The Supported Audio Stream Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 172: { type: "SupportedCameraRecordingConfiguration", // HomeKit sche: "supportedCameraRecordingConfiguration", // HomeKit characteristic: Characteristic.SupportedCameraRecordingConfiguration, deprecated: false, UUID: "00000205-0000-1000-8000-0026BB765291", description: "The Supported Audio Recording Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 173: { type: "SupportedDataStreamTransportConfiguration", // HomeKit-DataStream sche: "supportedDataStreamTransportConfiguration", // HomeKit-DataStream characteristic: Characteristic.SupportedDataStreamTransportConfiguration, deprecated: false, UUID: "00000130-0000-1000-8000-0026BB765291", description: "The Supported Data Stream Transport Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 174: { type: "SupportedCharacteristicValueTransitionConfiguration", // HomeKit sche: "supportedCharacteristicValueTransitionConfiguration", // HomeKit characteristic: Characteristic.SupportedCharacteristicValueTransitionConfiguration, deprecated: false, UUID: "00000144-0000-1000-8000-0026BB765291", description: "The Supported Characteristic Value Transition Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 175: { type: "SupportedDiagnosticsSnapshot", // HomeKitTypes.d.ts sche: "supportedDiagnosticsSnapshot", // HomeKitTypes.d.ts characteristic: Characteristic.SupportedDiagnosticsSnapshot, deprecated: false, UUID: "00000238-0000-1000-8000-0026BB765291", description: "The Supported Diagnostic Snapshot", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 176: { type: "SupportedRTPConfiguration", sche: "supportedRTPConfiguration", characteristic: Characteristic.SupportedRTPConfiguration, deprecated: false, UUID: "00000116-0000-1000-8000-0026BB765291", description: "The Supported RTP Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 177: { type: "SupportedRouterConfiguration", // HomeKitTypes.d.ts sche: "supportedRouterConfiguration", // HomeKitTypes.d.ts characteristic: Characteristic.SupportedRouterConfiguration, deprecated: false, UUID: "00000210-0000-1000-8000-0026BB765291", description: "The Supported Router Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 178: { type: "SupportedTransferTransportConfiguration", // HomeKit sche: "supportedTransferTransportConfiguration", // HomeKit characteristic: Characteristic.SupportedTransferTransportConfiguration, deprecated: false, UUID: "00000202-0000-1000-8000-0026BB765291", description: "The Supported Transfer Transport Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 179: { type: "SupportedVideoRecordingConfiguration", sche: "supportedVideoRecordingConfiguration", characteristic: Characteristic.SupportedVideoRecordingConfiguration, deprecated: false, UUID: "00000206-0000-1000-8000-0026BB765291", description: "The Supported Video Recording Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 180: { type: "SupportedVideoStreamConfiguration", sche: "supportedVideoStreamConfiguration", characteristic: Characteristic.SupportedVideoStreamConfiguration, deprecated: false, UUID: "00000114-0000-1000-8000-0026BB765291", description: "The Supported Video Stream Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 181: { type: "SwingMode", sche: "swingMode", characteristic: Characteristic.SwingMode, deprecated: false, UUID: "000000B6-0000-1000-8000-0026BB765291", description: "The DevicesnCurrent Swing Mode", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"SWING_DISABLED": Characteristic.SwingMode.SWING_DISABLED, "SWING_ENABLED": Characteristic.SwingMode.SWING_ENABLED } }, 182: { type: "TargetAirPurifierState", sche: "targetAirPurifierState", characteristic: Characteristic.TargetAirPurifierState, deprecated: false, UUID: "000000A8-0000-1000-8000-0026BB765291", description: "The Requested Target Air Purification State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentAirPurifierState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: // Checked 04/24/2020 {"MANUAL": Characteristic.TargetAirPurifierState.MANUAL, "AUTO": Characteristic.TargetAirPurifierState.AUTO } }, // @deprecated Removed and not used anymore as of homebridge v2 183: { type: "TargetAirQuality", sche: "targetAirQuality", characteristic: Characteristic.TargetAirQuality, deprecated: true, UUID: "000000AE-0000-1000-8000-0026BB765291", description: "The Requested Target Air Quality", relatedCurrentAccTypeEnumIndex: // @deprecated Removed and not used anymore as of homebridge v2 null, // CMD4_ACC_TYPE_ENUM.AirQuality, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: { // @deprecated Removed and not used anymore as of homebridge v2 // "EXCELLENT": Characteristic.TargetAirQuality.EXCELLENT, // "GOOD": Characteristic.TargetAirQuality.GOOD, // "FAIR": Characteristic.TargetAirQuality.FAIR } }, 184: { type: "TargetControlList", // Homekit-Remote sche: "targetControlList", // Homekit-Remote characteristic: Characteristic.TargetControlList, deprecated: false, UUID: "00000124-0000-1000-8000-0026BB765291", description: "The Target Control List", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_WRITE, hapPerms.PAIRED_READ, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 185: { type: "TargetControlSupportedConfiguration", // Homekit-Remote sche: "targetControlSupportedConfiguration", // Homekit-Remote characteristic: Characteristic.TargetControlSupportedConfiguration, deprecated: false, UUID: "00000123-0000-1000-8000-0026BB765291", description: "The Target Supported Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 186: { type: "TargetDoorState", sche: "targetDoorState", characteristic: Characteristic.TargetDoorState, deprecated: false, UUID: "00000032-0000-1000-8000-0026BB765291", description: "The Doors Requested State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentDoorState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"OPEN": Characteristic.TargetDoorState.OPEN, "CLOSED": Characteristic.TargetDoorState.CLOSED } }, 187: { type: "TargetFanState", sche: "targetFanState", characteristic: Characteristic.TargetFanState, deprecated: false, UUID: "000000BF-0000-1000-8000-0026BB765291", description: "The Fans Requested State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentFanState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"MANUAL": Characteristic.TargetFanState.MANUAL, "AUTO": Characteristic.TargetFanState.AUTO } }, 188: { type: "TargetHeaterCoolerState", sche: "targetHeaterCoolerState", characteristic: Characteristic.TargetHeaterCoolerState, deprecated: false, UUID: "000000B2-0000-1000-8000-0026BB765291", description: "The Heaters Requested Cooling State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentHeaterCoolerState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"AUTO": Characteristic.TargetHeaterCoolerState.AUTO, "HEAT": Characteristic.TargetHeaterCoolerState.HEAT, "COOL": Characteristic.TargetHeaterCoolerState.COOL } }, 189: { type: "TargetHeatingCoolingState", sche: "targetHeatingCoolingState", characteristic: Characteristic.TargetHeatingCoolingState, deprecated: false, UUID: "00000033-0000-1000-8000-0026BB765291", description: "The Heaters Requested Heating State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentHeatingCoolingState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"OFF": Characteristic.TargetHeatingCoolingState.OFF, "HEAT": Characteristic.TargetHeatingCoolingState.HEAT, "COOL": Characteristic.TargetHeatingCoolingState.COOL, "AUTO": Characteristic.TargetHeatingCoolingState.AUTO } }, 190: { type: "TargetHorizontalTiltAngle", sche: "targetHorizontalTiltAngle", characteristic: Characteristic.TargetHorizontalTiltAngle, deprecated: false, UUID: "0000007B-0000-1000-8000-0026BB765291", description: "The Requested Horizontal Tilt Angle", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentHorizontalTiltAngle, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 90, minValue: -90, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 191: { type: "TargetHumidifierDehumidifierState", sche: "targetHumidifierDehumidifierState", characteristic: Characteristic.TargetHumidifierDehumidifierState, deprecated: false, UUID: "000000B4-0000-1000-8000-0026BB765291", description: "The Requested Humidifier/DeHumidifier State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentHumidifierDehumidifierState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"HUMIDIFIER_OR_DEHUMIDIFIER": Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER, "HUMIDIFIER": Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER, "DEHUMIDIFIER": Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER } }, 192: { type: "TargetMediaState", // HomeKitTypes-Television sche: "targetMediaState", // HomeKitTypes-Television characteristic: Characteristic.TargetMediaState, deprecated: false, UUID: "00000137-0000-1000-8000-0026BB765291", description: "The Requested Media State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentMediaState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"PLAY": Characteristic.TargetMediaState.PLAY, "PAUSE": Characteristic.TargetMediaState.PAUSE, "STOP": Characteristic.TargetMediaState.STOP } }, 193: { type: "TargetPosition", sche: "targetPosition", characteristic: Characteristic.TargetPosition, deprecated: false, UUID: "0000007C-0000-1000-8000-0026BB765291", description: "The Devices Requested Position", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentPosition, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 194: { type: "TargetRelativeHumidity", sche: "targetRelativeHumidity", characteristic: Characteristic.TargetRelativeHumidity, deprecated: false, UUID: "00000034-0000-1000-8000-0026BB765291", description: "The Devices Requested Relative Humidity Level", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentRelativeHumidity, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, // @deprecated Removed and not used anymore as of homebridge v2 195: { type: "TargetSlatState", sche: "targetSlatState", characteristic: Characteristic.TargetSlatState, deprecated: true, UUID: "000000BE-0000-1000-8000-0026BB765291", description: "The Devices Requested Slat State", relatedCurrentAccTypeEnumIndex: // @deprecated Removed and not used anymore as of homebridge v2 null, // CMD4_ACC_TYPE_ENUM.CurrentSlatState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: { // @deprecated Removed and not used anymore as of homebridge v2 // "MANUAL": Characteristic.TargetSlatState.MANUAL, // "AUTO": Characteristic.TargetSlatState.AUTO } }, 196: { type: "TargetTemperature", sche: "targetTemperature", characteristic: Characteristic.TargetTemperature, deprecated: false, UUID: "00000035-0000-1000-8000-0026BB765291", description: "The Devices Requested Temperature", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentTemperature, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, unit: hapUnits.CELSIUS, maxValue: 38, minValue: 10, minStep: 0.1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 197: { type: "TargetTiltAngle", sche: "targetTiltAngle", characteristic: Characteristic.TargetTiltAngle, deprecated: false, UUID: "000000C2-0000-1000-8000-0026BB765291", description: "The Devices Requested Tilt Angle", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentTiltAngle, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 90, minValue: -90, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 198: { type: "TargetVerticalTiltAngle", sche: "targetVerticalTiltAngle", characteristic: Characteristic.TargetVerticalTiltAngle, deprecated: false, UUID: "0000007D-0000-1000-8000-0026BB765291", description: "The Devices Requested Vertical Tilt Angle", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentVerticalTiltAngle, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, unit: hapUnits.ARC_DEGREE, maxValue: 90, minValue: -90, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 199: { type: "TargetVisibilityState", // HomeKittypes-Television sche: "targetVisibilityState", // HomeKittypes-Television characteristic: Characteristic.TargetVisibilityState, deprecated: false, UUID: "00000134-0000-1000-8000-0026BB765291", description: "The Devices Requested Visibility State", relatedCurrentAccTypeEnumIndex: CMD4_ACC_TYPE_ENUM.CurrentVisibilityState, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"SHOWN": Characteristic.TargetVisibilityState.SHOWN, "HIDDEN": Characteristic.TargetVisibilityState.HIDDEN } }, 200: { type: "TemperatureDisplayUnits", sche: "temperatureDisplayUnits", characteristic: Characteristic.TemperatureDisplayUnits, deprecated: false, UUID: "00000036-0000-1000-8000-0026BB765291", description: "The Units to Display the Temperature in", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"CELSIUS": Characteristic.TemperatureDisplayUnits.CELSIUS, "FAHRENHEIT": Characteristic.TemperatureDisplayUnits.FAHRENHEIT } }, 201: { type: "ThirdPartyCameraActive", // HomeKitTypes.d.ts sche: "thirdPartyCameraActive", // HomeKitTypes.d.ts characteristic: Characteristic.ThirdPartyCameraActive, deprecated: false, UUID: "0000021C-0000-1000-8000-0026BB765291", description: "The ON/OFF Auxiliary Camera State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: { "OFF" : Characteristic.ThirdPartyCameraActive.OFF, // 0 "ON" : Characteristic.ThirdPartyCameraActive.ON // 1 } }, // @deprecated Removed and not used anymore as of homebridge v2 202: { type: "TimeUpdate", // HomeKitTypes-Bridge sche: "timeUpdate", // HomeKitTypes-Bridge characteristic: Characteristic.TimeUpdate, deprecated: true, UUID: "0000009A-0000-1000-8000-0026BB765291", description: "Ifnthe Time Should be Updated", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 203: { type: "TransmitPower", // HomeKitTypes sche: "transmitPower", // HomeKitTypes characteristic: Characteristic.TransmitPower, deprecated: false, UUID: "00000242-0000-1000-8000-0026BB765291", description: "The Devices Measured Transmit Power", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 204: { type: "MaximumTransmitPower", sche: "MaximumTransmitPower", characteristic: Characteristic.MaximumTransmitPower, deprecated: false, UUID: "00000243-0000-1000-8000-0026BB765291", description: "The Devices Maximum Transmit Power", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 205: { type: "TunnelConnectionTimeout", // HomeKitTypes-Bridge sche: "tunnelConnectionTimeout", // HomeKitTypes-Bridge characteristic: Characteristic.TunnelConnectionTimeout, deprecated: false, UUID: "00000061-0000-1000-8000-0026BB765291", description: "The Timeout Value of the Tunnel Connection", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: {} }, 206: { type: "TunneledAccessoryAdvertising", // HomeKitTypes-Bridge sche: "tunneledAccessoryAdvertising", // HomeKitTypes-Bridge characteristic: Characteristic.TunneledAccessoryAdvertising, deprecated: false, UUID: "00000060-0000-1000-8000-0026BB765291", description: "If the Device is Currently Tunneled Advertising", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_WRITE, // Reversed in HomeBridge hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 207: { type: "TunneledAccessoryConnected", // HomeKitTypes-Bridge sche: "tunneledAccessoryConnected", // HomeKitTypes-Bridge characteristic: Characteristic.TunneledAccessoryConnected, deprecated: false, UUID: "00000059-0000-1000-8000-0026BB765291", description: "If the Device is Tunneled Connected ", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {"FALSE": 0, "TRUE": 1 } }, 208: { type: "TunneledAccessoryStateNumber", // HomeKitTypes-Bridge sche: "tunneledAccessoryStateNumber", // HomeKitTypes-Bridge characteristic: Characteristic.TunneledAccessoryStateNumber, deprecated: false, UUID: "00000058-0000-1000-8000-0026BB765291", description: "The Tunneled Accessorys State Number", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.INT, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 209: { type: "VOCDensity", sche: "vocDensity", characteristic: Characteristic.VOCDensity, deprecated: false, UUID: "000000C8-0000-1000-8000-0026BB765291", description: "The Devices Measured VOC Density", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 1000, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 210: { type: "ValveType", sche: "valveType", characteristic: Characteristic.ValveType, deprecated: false, UUID: "000000D5-0000-1000-8000-0026BB765291", description: "The Devices Valve Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"GENERIC_VALVE": Characteristic.ValveType.GENERIC_VALVE, "IRRIGATION": Characteristic.ValveType.IRRIGATION, "SHOWER_HEAD": Characteristic.ValveType.SHOWER_HEAD, "WATER_FAUCET": Characteristic.ValveType.WATER_FAUCET } }, 211: { type: "Version", sche: "version", characteristic: Characteristic.Version, deprecated: false, UUID: "00000037-0000-1000-8000-0026BB765291", description: "The Devices Version String", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 212: { type: "VideoAnalysisActive", sche: "videoAnalysisActive", characteristic: Characteristic.VideoAnalysisActive, deprecated: false, UUID: "00000229-0000-1000-8000-0026BB765291", description: "The Devices Video Analysis Active Status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 213: { type: "Volume", sche: "volume", characteristic: Characteristic.Volume, deprecated: false, UUID: "00000119-0000-1000-8000-0026BB765291", description: "The Devices Volume as a Percentage", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, unit: hapUnits.PERCENTAGE, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY ] }, validValues: {} }, 214: { type: "VolumeControlType", // HomeKitTypes-Television sche: "volumeControlType", // HomeKitTypes-Television characteristic: Characteristic.VolumeControlType, deprecated: false, UUID: "000000E9-0000-1000-8000-0026BB765291", description: "The Devices Volume Control Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 3, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {"NONE": Characteristic.VolumeControlType.NONE, "RELATIVE": Characteristic.VolumeControlType.RELATIVE, "RELATIVE_WITH_CURRENT": Characteristic.VolumeControlType.RELATIVE_WITH_CURRENT, "ABSOLUTE": Characteristic.VolumeControlType.ABSOLUTE } }, 215: { type: "VolumeSelector", // HomeKitTypes-Television sche: "volumeSelector", // HomeKitTypes-Television characteristic: Characteristic.VolumeSelector, deprecated: false, UUID: "000000EA-0000-1000-8000-0026BB765291", description: "The Devices Volume Selector", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_WRITE ] }, validValues: {"INCREMENT": Characteristic.VolumeSelector.INCREMENT, "DECREMENT": Characteristic.VolumeSelector.DECREMENT } }, 216: { type: "WANConfigurationList", // HomeKitTypes.d.ts sche: "wanConfigurationList", // HomeKitTypes.d.ts characteristic: Characteristic.WANConfigurationList, deprecated: false, UUID: "00000211-0000-1000-8000-0026BB765291", description: "The Devices WAN Configuration List", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 217: { type: "WANStatusList", // HomeKitTypes.d.ts sche: "wanStatusList", // HomeKitTypes.d.ts characteristic: Characteristic.WANStatusList, deprecated: false, UUID: "00000212-0000-1000-8000-0026BB765291", description: "The Devices WAN Status List", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 218: { type: "WakeConfiguration", // HomeKitTypes.d.ts sche: "wakeConfiguration", // HomeKitTypes.d.ts characteristic: Characteristic.WakeConfiguration, deprecated: false, UUID: "00000222-0000-1000-8000-0026BB765291", description: "The Devices Wake Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 219: { type: "WaterLevel", sche: "waterLevel", characteristic: Characteristic.WaterLevel, deprecated: false, UUID: "000000B5-0000-1000-8000-0026BB765291", description: "The Current Water Level Measurement", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToFloat( str ); }, props: {format: hapFormats.FLOAT, allowedWordCount: 1, maxValue: 100, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: {} }, 220: { type: "WiFiCapabilities", // HomeKit sche: "wifiCapabilities", // HomeKit characteristic: Characteristic.WiFiCapabilities, deprecated: false, UUID: "0000022C-0000-1000-8000-0026BB765291", description: "The Devices WiFi Capabilities", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: {} }, 221: { type: "WiFiConfigurationControl", // HomeKit sche: "wifiConfigurationControl", // HomeKit characteristic: Characteristic.WiFiConfigurationControl, deprecated: false, UUID: "0000022D-0000-1000-8000-0026BB765291", description: "The Devices WiFi Configuration Control", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.NOTIFY, hapPerms.TIMED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: {} }, 222: { type: "WiFiSatelliteStatus", // HomeKitTypes.d.ts sche: "wifiSatelliteStatus", // HomeKitTypes.d.ts characteristic: Characteristic.WiFiSatelliteStatus, deprecated: false, UUID: "0000021E-0000-1000-8000-0026BB765291", description: "The Devices WiFi Satellite Status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 2, minValue: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: { "UNKNOWN": Characteristic.WiFiSatelliteStatus.UNKNOWN, // 0, - Check "CONNECTED" : Characteristic.WiFiSatelliteStatus.CONNECTED, // 1, "NOT_CONNECTED": Characteristic.WiFiSatelliteStatus.NOT_CONNECTED // 2 } }, 223: { type: "AssetUpdateReadiness", sche: "assetUpdateReadiness", characteristic: Characteristic.AssetUpdateReadiness, deprecated: false, UUID: "00000269-0000-1000-8000-0026BB765291", description: "The Devices Asset Update Readiness", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: { } }, 224: { type: "SupportedAssetTypes", sche: "supportedAssetTypes", characteristic: Characteristic.SupportedAssetTypes, deprecated: false, UUID: "00000268-0000-1000-8000-0026BB765291", description: "The Devices Supported Asset types", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 225: { type: "ConfigurationState", sche: "configurationState", characteristic: Characteristic.ConfigurationState, deprecated: false, UUID: "00000263-0000-1000-8000-0026BB765291", description: "The Devices Configuration State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT16, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: { } }, 226: { type: "NFCAccessControlPoint", sche: "NFCAccessControlPoint", characteristic: Characteristic.NFCAccessControlPoint, deprecated: false, UUID: "00000264-0000-1000-8000-0026BB765291", description: "The NFC Access Point", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: { } }, 227: { type: "NFCAccessSupportedConfiguration", sche: "NFCAccessSupportedConfiguration", characteristic: Characteristic.NFCAccessSupportedConfiguration, deprecated: false, UUID: "00000265-0000-1000-8000-0026BB765291", description: "The Devices NFC Access Support Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 228: { type: "SiriEndpointSessionStatus", sche: "SiriEndpointSessionStatus", characteristic: Characteristic.SiriEndpointSessionStatus, deprecated: false, UUID: "00000254-0000-1000-8000-0026BB765291", description: "The Devices Siri endpoint", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: { } }, 229: { type: "ThreadControlPoint", sche: "threadControlPoint", characteristic: Characteristic.ThreadControlPoint, deprecated: false, UUID: "00000704-0000-1000-8000-0026BB765291", description: "The Devices thread control point", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_WRITE ] }, validValues: { } }, 230: { type: "ThreadNodeCapabilities", sche: "threadNodeCapabilities", characteristic: Characteristic.ThreadNodeCapabilities, deprecated: false, UUID: "00000702-0000-1000-8000-0026BB765291", description: "The Devices thread node capabilities", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT16, allowedWordCount: 1, maxValue: 31, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 231: { type: "ThreadStatus", sche: "threadStatus", characteristic: Characteristic.ThreadStatus, deprecated: false, UUID: "00000703-0000-1000-8000-0026BB765291", description: "The Devices thread status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT16, allowedWordCount: 1, maxValue: 6, minValue: 0, minStep: 1, perms: [hapPerms.PAIRED_READ, hapPerms.NOTIFY ] }, validValues: { } }, 232: { type: "ThreadOpenThreadVersion", sche: "threadOpenThreadVersion", characteristic: Characteristic.ThreadOpenThreadVersion, deprecated: false, UUID: "00000706-0000-1000-8000-0026BB765291", description: "The Devices thread version", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, // Added Mar 2024 233: { type: "AccessCodeControlPoint", sche: "accessCodeControlPoint", characteristic: Characteristic.AccessCodeControlPoint, deprecated: false, UUID: "00000262-0000-1000-8000-0026BB765291", description: "Access Code Control Point", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: { } }, 234: { type: "AccessCodeSupportedConfiguration", sche: "accessCodeSupportedConfiguration", characteristic: Characteristic.AccessCodeSupportedConfiguration, deprecated: false, UUID: "00000261-0000-1000-8000-0026BB765291", description: "Access Code Supported Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 235: { type: "AirPlayEnable", sche: "airPlayEnable", characteristic: Characteristic.AirPlayEnable, deprecated: false, UUID: "0000025B-0000-1000-8000-0026BB765291", description: "Airplay Enable", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: { } }, 236: { type: "CharacteristicValueActiveTransitionCount", sche: "characteristicValueActiveTransitionCount", characteristic: Characteristic.CharacteristicValueActiveTransitionCount, deprecated: false, UUID: "0000024B-0000-1000-8000-0026BB765291", description: "Characteristic Value Active Transition Count", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ ] }, validValues: { } }, 237: { type: "CryptoHash", sche: "cryptoHash", characteristic: Characteristic.CryptoHash, deprecated: false, UUID: "00000250-0000-1000-8000-0026BB765291", description: "Crypto Hash", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: { } }, 238: { type: "FirmwareUpdateReadiness", sche: "firmwareUpdateReadiness", characteristic: Characteristic.FirmwareUpdateReadiness, deprecated: false, UUID: "00000234-0000-1000-8000-0026BB765291", description: "Firmware Update Readiness", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ ] }, validValues: { } }, 239: { type: "FirmwareUpdateStatus", sche: "firmwareUpdateStatus", characteristic: Characteristic.FirmwareUpdateStatus, deprecated: false, UUID: "00000235-0000-1000-8000-0026BB765291", description: "The Devices firmware update status", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ ] }, validValues: { } }, 240: { type: "HardwareFinish", sche: "hardwareFinish", characteristic: Characteristic.HardwareFinish, deprecated: false, UUID: "0000026C-0000-1000-8000-0026BB765291", description: "Hardware Finish", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 241: { type: "MetricsBufferFullState", sche: "MetricsBufferFullState", characteristic: Characteristic.MetricsBufferFullState, deprecated: false, UUID: "00000272-0000-1000-8000-0026BB765291", description: "Metrics Buffer Full State", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToBool( str ); }, props: {format: hapFormats.BOOL, allowedWordCount: 1, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ ] }, validValues: { } }, 242: { type: "MultifunctionButton", sche: "multiFunctionButton", characteristic: Characteristic.MultifunctionButton, deprecated: false, UUID: "0000026B-0000-1000-8000-0026BB765291", description: "Multifunction Button", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ ] }, validValues: { } }, 243: { type: "SelectedDiagnosticsModes", sche: "selectedDiagnosticsModes", characteristic: Characteristic.SelectedDiagnosticsModes, deprecated: false, UUID: "0000024D-0000-1000-8000-0026BB765291", description: "Selected Diagnostics Modes", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT32, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: { } }, 244: { type: "SelectedSleepConfiguration", sche: "selectedSleepConfiguration", characteristic: Characteristic.SelectedSleepConfiguration, deprecated: false, UUID: "00000252-0000-1000-8000-0026BB765291", description: "Selected Sleep Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE, hapPerms.WRITE_RESPONSE ] }, validValues: { } }, 245: { type: "SiriEnable", sche: "siriEnable", characteristic: Characteristic.SiriEnable, deprecated: false, UUID: "00000255-0000-1000-8000-0026BB765291", description: "Siri Enable", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: { } }, 246: { type: "SiriEngineVersion", sche: "siriEngineVersion", characteristic: Characteristic.SiriEngineVersion, deprecated: false, UUID: "0000025A-0000-1000-8000-0026BB765291", description: "Siri's engine version", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 247: { type: "SiriLightOnUse", sche: "siriLightOnUse", characteristic: Characteristic.SiriLightOnUse, deprecated: false, UUID: "00000258-0000-1000-8000-0026BB765291", description: "Siri Light on Use", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: { } }, 248: { type: "SiriListening", sche: "siriListening", characteristic: Characteristic.SiriListening, deprecated: false, UUID: "00000256-0000-1000-8000-0026BB765291", description: "Siri Listening", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: { } }, 249: { type: "SiriTouchToUse", sche: "siriTouchToUse", characteristic: Characteristic.SiriTouchToUse, deprecated: false, UUID: "00000257-0000-1000-8000-0026BB765291", description: "Siri Touch to use", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT8, allowedWordCount: 1, maxValue: 1, minValue: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: { } }, 250: { type: "StagedFirmwareVersion", sche: "StagedFirmwareVersion", characteristic: Characteristic.StagedFirmwareVersion, deprecated: false, UUID: "00000249-0000-1000-8000-0026BB765291", description: "Staged Firmware Version", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToString( str ); }, props: {format: hapFormats.STRING, allowedWordCount: 0, perms: [hapPerms.NOTIFY, hapPerms.PAIRED_READ ] }, validValues: { } }, 251: { type: "SupportedMetrics", sche: "supportedMetrics", characteristic: Characteristic.SupportedMetrics, deprecated: false, UUID: "00000271-0000-1000-8000-0026BB765291", description: "Supported Metrics", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ, hapPerms.PAIRED_WRITE ] }, validValues: { } }, 252: { type: "SupportedFirmwareUpdateConfiguration", sche: "supportedFirmwareUpdateConfiguration", characteristic: Characteristic.SupportedFirmwareUpdateConfiguration, deprecated: false, UUID: "00000233-0000-1000-8000-0026BB765291", description: "Supported Firmware Update Configuration", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToTLV8( str ); }, props: {format: hapFormats.TLV8, allowedWordCount: 0, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 253: { type: "TapType", sche: "tapType", characteristic: Characteristic.TapType, deprecated: false, UUID: "0000022F-0000-1000-8000-0026BB765291", description: "The devices Tap Type", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringToInt( str ); }, props: {format: hapFormats.UINT16, allowedWordCount: 1, perms: [hapPerms.PAIRED_READ ] }, validValues: { } }, 254: { type: "Token", sche: "token", characteristic: Characteristic.Token, deprecated: false, UUID: "00000231-0000-1000-8000-0026BB765291", description: "The devices Token", relatedCurrentAccTypeEnumIndex: null, relatedTargetAccTypeEnumIndex: null, stringConversionFunction: function ( str ){ return stringPassThru( str ); }, props: {format: hapFormats.DATA, allowedWordCount: 0, perms: [hapPerms.PAIRED_WRITE ] }, validValues: { } } }; return CMD4_ACC_TYPE_ENUM; }, CMD4_ACC_TYPE_ENUM } ================================================ FILE: lib/CMD4_CHAR_TYPE_ENUMS.js ================================================ 'use strict'; var CMD4_CHAR_TYPE_ENUMS = { CMD4_FORMAT_TYPE_ENUM: { BOOL: 0, INT: 1, FLOAT: 2, STRING: 3, UINT8: 4, UINT16: 5, UINT32: 6, UINT64: 7, DATA: 8, TLV8: 9, DICTIONARY: 10, EOL: 11, properties: { } }, CMD4_UNITS_TYPE_ENUM: { CELSIUS: 0, PERCENTAGE: 1, ARC_DEGREE: 2, LUX: 3, SECONDS: 4, EOL: 5, properties: { } }, CMD4_PERMS_TYPE_ENUM: { READ: 0, WRITE: 1, PAIRED_READ: 2, PAIRED_WRITE: 3, NOTIFY: 4, ADDITIONAL_AUTHORIZATION: 5, TIMED_WRITE: 6, HIDDEN: 7, WRITE_RESPONSE: 8, EOL: 9, properties: { } } } // Export both the init function and the uninitialized data for unit testing module.exports = { init: function ( hapFormats, hapUnits, hapPerms ) { // Fill in the properties of all possible characteristics // props was added because calling getCharacteridtic().props.perms adds // the characteristic in by default. This costs some lines, but is advantageous. CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.properties = { 0: { type: hapFormats.BOOL // "bool" }, 1: { type: hapFormats.INT // "int" }, 2: { type: hapFormats.FLOAT // "float" }, 3: { type: hapFormats.STRING // "string" }, 4: { type: hapFormats.UINT8 // "uint8" }, 5: { type: hapFormats.UINT16 // "uint16" }, 6: { type: hapFormats.UINT32 // "uint32" }, 7: { type: hapFormats.UINT64 // "uint64" }, 8: { type: hapFormats.DATA // "data" }, 9: { type: hapFormats.TLV8 // "tlv8" }, 10: { type: hapFormats.ARRAY // "array" }, 11: { type: hapFormats.DICTIONARY // "dict" } }; CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.properties = { 0: { type: hapUnits.CELSIUS // "celsius" }, 1: { type: hapUnits.PERCENTAGE // "percentage" }, 2: { type: hapUnits.ARC_DEGREE // "arcdegrees" }, 3: { type: hapUnits.LUX // "lux" }, 4: { type: hapUnits.SECONDS // "seconds" } }; CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.properties = { 0: { type: hapPerms.READ // "pr" }, 1: { type: hapPerms.WRITE // "pw" }, 2: { type: hapPerms.PAIRED_READ // "pr" }, 3: { type: hapPerms.PAIRED_WRITE // "pw" }, 4: { type: hapPerms.NOTIFY // "ev" }, 5: { type: hapPerms.EVENTS // "ev" }, 6: { type: hapPerms.ADDITIONAL_AUTHORIZATION // "aa" }, 7: { type: hapPerms.TIMED_WRITE // "tw" }, 8: { type: hapPerms.HIDDEN // "hd" }, 9: { type: hapPerms.WRITE_RESPONSE // "wr" } }; return CMD4_CHAR_TYPE_ENUMS; }, CMD4_CHAR_TYPE_ENUMS } ================================================ FILE: lib/CMD4_DEVICE_TYPE_ENUM.js ================================================ 'use strict'; const constants = require( "../cmd4Constants" ); // The sObject.defineProperty is to resolve a lint issue. // See utils/indexOfEnumLintTest.js for further information. let indexOfEnum = require( "../utils/indexOfEnum" ); Object.defineProperty( exports, "indexOfEnum", { enumerable: true, get: function ( ){ return indexOfEnum.indexOfEnum; } }); var CMD4_DEVICE_TYPE_ENUM = { AccessControl: 0, AccessoryRuntimeInformation: 1, AccessoryInformation: 2, AirPurifier: 3, AirQualitySensor: 4, BatteryService: 5, BridgeConfiguration: 6, BridgingState: 7, CamaeraEventRecordingManagement: 8, CameraControl: 9, CameraRTPStreamManagement: 10, CameraOperatingMode: 11, CarbonDioxideSensor: 12, CarbonMonoxideSensor: 13, ContactSensor: 14, Diagnostics: 15, Door: 16, DoorBell: 17, Fan: 18, Fanv1: 19, Fanv2: 20, Faucet: 21, FilterMaintenance: 22, GarageDoorOpener: 23, HeaterCooler: 24, HumidifierDehumidifier: 25, HumiditySensor: 26, InputSource: 27, IrrigationSystem: 28, LeakSensor: 29, LightSensor: 30, Lightbulb: 31, LockManagement: 32, LockMechanism: 33, Microphone: 34, MotionSensor: 35, OccupancySensor: 36, Outlet: 37, Pairing: 38, PowerManagement: 39, ProtocolInformation: 40, Relay: 41, SecuritySystem: 42, ServiceLabel: 43, Siri: 44, Slats: 45, SmartSpeaker: 46, SmokeSensor: 47, Speaker: 48, StatefulProgrammableSwitch: 49, StatelessProgrammableSwitch: 50, Switch: 51, TargetControl: 52, TargetControlManagement: 53, Television: 54, TelevisionSpeaker: 55, TemperatureSensor: 56, Thermostat: 57, TimeInformation: 58, TransferTransportManagement: 59, Tunnel: 60, Valve: 61, WiFiRouter: 62, WiFiSatellite: 63, Window: 64, WindowCovering: 65, AccessoryMetrics: 66, AssetUpdate: 67, Assistant: 68, AudioStreamManagement: 69, Battery: 70, CameraRecordingManagement: 71, CloudRelay: 72, DataStreamTransportManagement: 73, NFCAccess: 74, SiriEndpoint: 75, ThreadTransport: 76, // New Mar 2024 AccessCode: 77, FirmwareUpdate: 78, TapManagement: 79, WiFiTransport: 80, EOL: 81, properties:{}, devEnumIndexToC: function( index ) { return CMD4_DEVICE_TYPE_ENUM.properties[ index ].deviceName; }, indexOfEnum: function( deviceName ) { return CMD4_DEVICE_TYPE_ENUM.properties.indexOfEnum( i => i.deviceName === deviceName ); }, }; // Export both the init function and the uninitialized data for unit testing module.exports = { init: function ( CMD4_ACC_TYPE_ENUM, Service, Characteristic, Categories ) { // Fill in the properties of each device (Must be done at runtime) CMD4_DEVICE_TYPE_ENUM.properties = { 0: { deviceName:'AccessControl', deprecated: false, UUID: "000000DA-0000-1000-8000-0026BB765291", service: Service.AccessControl, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.AccessControlLevel, defaultValue: 0, // min 0, max 2 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.PasswordSetting ], defaultPollingCharacteristics: [] }, 1: { deviceName:'AccessoryRuntimeInformation', deprecated: false, UUID: "00000239-0000-1000-8000-0026BB765291", service: Service.AccessoryRuntimeInformation, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Ping, defaultValue: 0, // Type is DATA, therefore Who Knows relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.ActivityInterval, CMD4_ACC_TYPE_ENUM.HeartBeat, CMD4_ACC_TYPE_ENUM.SleepInterval ], defaultPollingCharacteristics: [] }, 2: { deviceName:'AccessoryInformation', deprecated: false, UUID: "0000003E-0000-1000-8000-0026BB765291", service: Service.AccessoryInformation, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Identify, defaultValue: 1, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Manufacturer, defaultValue: 'Cmd4', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Model, defaultValue: 'Model', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Name, defaultValue: 'My_AccessoryInformation', relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SerialNumber, defaultValue: 'ABC001', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.FirmwareRevision, defaultValue: '100.1.1', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.HardwareRevision, CMD4_ACC_TYPE_ENUM.AccessoryFlags ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Identify ] }, 3: { deviceName:'AirPurifier', deprecated: false, UUID: "000000BB-0000-1000-8000-0026BB765291", service: Service.AirPurifier, defaultCategory: Categories.AIR_PURIFIER, publishExternally: false, devicesStateChangeDefaultTime: constants.SLOW_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] }, {type: CMD4_ACC_TYPE_ENUM.CurrentAirPurifierState, defaultValue: Characteristic.CurrentAirPurifierState.PURIFYING_AIR, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetAirPurifierState ] }, {type: CMD4_ACC_TYPE_ENUM.TargetAirPurifierState, defaultValue: Characteristic.TargetAirPurifierState.AUTO, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentAirPurifierState ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.LockPhysicalControls, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.SwingMode, CMD4_ACC_TYPE_ENUM.RotationSpeed ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 4: { deviceName:'AirQualitySensor', deprecated: false, UUID: "0000008D-0000-1000-8000-0026BB765291", service: Service.AirQualitySensor, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.FAST_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.AirQuality, defaultValue: Characteristic.AirQuality.GOOD, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.OzoneDensity, CMD4_ACC_TYPE_ENUM.NitrogenDioxideDensity, CMD4_ACC_TYPE_ENUM.SulphurDioxideDensity, CMD4_ACC_TYPE_ENUM.PM2_5Density, CMD4_ACC_TYPE_ENUM.PM10Density, CMD4_ACC_TYPE_ENUM.VOCDensity, CMD4_ACC_TYPE_ENUM.CarbonMonoxideLevel, CMD4_ACC_TYPE_ENUM.CarbonDioxideLevel ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.AirQuality ] }, 5: { deviceName:'BatteryService', // Use Battery instead deprecated: true, UUID: "00000096-0000-1000-8000-0026BB765291", //service: Service.Battery, service: null, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ //{type: CMD4_ACC_TYPE_ENUM.BatteryLevel, // defaultValue: 50, // Range 0-100 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.ChargingState, // defaultValue: Characteristic.ChargingState.NOT_CHARGING, // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.StatusLowBattery, // defaultValue: Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //} ], optionalCharacteristics: [ //CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ //CMD4_ACC_TYPE_ENUM.StatusLowBattery ] }, // @deprecated Removed and not used anymore as of homebridge v2 6: { deviceName:'BridgeConfiguration', deprecated: true, UUID: "000000A1-0000-1000-8000-0026BB765291", // service: Service.BridgeConfiguration, service: null, // defaultCategory: Categories.BRIDGE, defaultCategory: null, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ //{type: CMD4_ACC_TYPE_ENUM.ConfigureBridgedAccessoryStatus, // defaultValue: 0, // Format: TLV8 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //} ], optionalCharacteristics: [ //CMD4_ACC_TYPE_ENUM.ConfigureBridgedAccessoryStatus, //CMD4_ACC_TYPE_ENUM.DiscoverBridgedAccessories, //CMD4_ACC_TYPE_ENUM.DiscoveredBridgedAccessories, //CMD4_ACC_TYPE_ENUM.ConfigureBridgedAccessory ], defaultPollingCharacteristics: [ //CMD4_ACC_TYPE_ENUM.ConfigureBridgedAccessoryStatus ] }, // @deprecated Removed and not used anymore as of homebridge v2 7: { deviceName:'BridgingState', deprecated: true, UUID: "00000062-0000-1000-8000-0026BB765291", // @deprecated Removed and not used anymore as of homebridge v2 // service: Service.BridgingState, service: null, // defaultCategory: Categories.BRIDGE, defaultCategory: null, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ //{type: CMD4_ACC_TYPE_ENUM.Reachable, // defaultValue: 1, // Format: Bool // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.LinkQuality, // defaultValue: 1, // Format: Uint8 // Range: 1-4, Step: 1 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.AccessoryIdentifier, // defaultValue: "id999", // Format: String // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.Category, // defaultValue: 16, // Format: Uint16 // Range: 1-16, Step 1 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //} ], optionalCharacteristics: [ //CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ //CMD4_ACC_TYPE_ENUM.Reachable ] }, 8: { deviceName:'CameraEventRecordingManagement', // Use CameraRecordingManagement deprecated: true, UUID: "00000204-0000-1000-8000-0026BB765291", //service: Service.CameraRecordingManagement, service: null, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ //{type: CMD4_ACC_TYPE_ENUM.Active, // defaultValue: Characteristic.Active.ACTIVE, // relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], // relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] //}, //{type: CMD4_ACC_TYPE_ENUM.SupportedCameraRecordingConfiguration, // defaultValue: 0, // Format: TLV8 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.SupportedVideoRecordingConfiguration, // defaultValue: 0, // Format: TLV8 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.SupportedAudioRecordingConfiguration, // defaultValue: 0, // Format: TLV8 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.SelectedCameraRecordingConfiguration, // defaultValue: 0, // Format: TLV8 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, ], optionalCharacteristics: [ //CMD4_ACC_TYPE_ENUM.RecordingAudioActive ], defaultPollingCharacteristics: [ //CMD4_ACC_TYPE_ENUM.Active ] }, // @deprecated Removed and not used anymore as of homebridge v2 9: { deviceName:'CameraControl', deprecated: true, UUID: "00000111-0000-1000-8000-0026BB765291", // service: Service.CameraControl, service: null, //defaultCategory: Categories.OTHER, defaultCategory: null, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ //{type: CMD4_ACC_TYPE_ENUM.On, // defaultValue: 1, // Format: Bool // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //} ], optionalCharacteristics: [ //CMD4_ACC_TYPE_ENUM.CurrentHorizontalTiltAngle, //CMD4_ACC_TYPE_ENUM.CurrentVerticalTiltAngle, //CMD4_ACC_TYPE_ENUM.TargetHorizontalTiltAngle, //CMD4_ACC_TYPE_ENUM.TargetVerticalTiltAngle, //CMD4_ACC_TYPE_ENUM.NightVision, //CMD4_ACC_TYPE_ENUM.OpticalZoom, //CMD4_ACC_TYPE_ENUM.DigitalZoom, //CMD4_ACC_TYPE_ENUM.ImageRotation, //CMD4_ACC_TYPE_ENUM.ImageMirroring, //CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ //CMD4_ACC_TYPE_ENUM.On ] }, 10: { deviceName:'CameraRTPStreamManagement', deprecated: false, UUID: "00000110-0000-1000-8000-0026BB765291", service: Service.CameraRTPStreamManagement, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SupportedVideoStreamConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedAudioStreamConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedRTPConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SelectedRTPStreamConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.StreamingStatus, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SetupEndpoints, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ ] }, 11: { deviceName:'CameraOperatingMode', deprecated: false, UUID: "0000021A-0000-1000-8000-0026BB765291", service: Service.CameraOperatingMode, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.EventSnapshotsActive, defaultValue: Characteristic.EventSnapshotsActive.DISABLE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.HomeKitCameraActive, defaultValue: Characteristic.HomeKitCameraActive.OFF, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.CameraOperatingModeIndicator, CMD4_ACC_TYPE_ENUM.ManuallyDisabled, CMD4_ACC_TYPE_ENUM.NightVision, CMD4_ACC_TYPE_ENUM.PeriodicSnapshotsActive, CMD4_ACC_TYPE_ENUM.ThirdPartyCameraActive, CMD4_ACC_TYPE_ENUM.DiagonalFieldOfView ], defaultPollingCharacteristics: [] }, 12: { deviceName:'CarbonDioxideSensor', deprecated: false, UUID: "00000097-0000-1000-8000-0026BB765291", service: Service.CarbonDioxideSensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CarbonDioxideDetected, defaultValue: Characteristic.CarbonDioxideDetected.CO2_LEVELS_NORMAL, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.CarbonDioxideLevel, CMD4_ACC_TYPE_ENUM.CarbonDioxidePeakLevel, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CarbonDioxideDetected ] }, 13: { deviceName:'CarbonMonoxideSensor', deprecated: false, UUID: "0000007F-0000-1000-8000-0026BB765291", service: Service.CarbonMonoxideSensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CarbonMonoxideDetected, defaultValue: Characteristic.CarbonMonoxideDetected.CO_LEVELS_NORMAL, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.CarbonMonoxideLevel, CMD4_ACC_TYPE_ENUM.CarbonMonoxidePeakLevel, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CarbonMonoxideDetected ] }, 14: { deviceName:'ContactSensor', deprecated: false, UUID: "00000080-0000-1000-8000-0026BB765291", service: Service.ContactSensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ContactSensorState, defaultValue: Characteristic.ContactSensorState.CONTACT_NOT_DETECTED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.ContactSensorState ] }, 15: { deviceName:'Diagnostics', deprecated: false, UUID: "00000237-0000-1000-8000-0026BB765291", service: Service.Diagnostics, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SupportedDiagnosticsSnapshot, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [] }, 16: { deviceName:'Door', deprecated: false, UUID: "00000081-0000-1000-8000-0026BB765291", service: Service.Door, defaultCategory: Categories.DOOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentPosition, defaultValue: 0, // Range 0 - 100 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetPosition ] }, {type: CMD4_ACC_TYPE_ENUM.PositionState, defaultValue: Characteristic.PositionState.STOPPED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TargetPosition, defaultValue: 0, // Range 0 - 100 relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentPosition ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.HoldPosition, CMD4_ACC_TYPE_ENUM.ObstructionDetected, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentPosition, CMD4_ACC_TYPE_ENUM.TargetPosition ] }, 17: { deviceName:'DoorBell', deprecated: false, UUID: "00000121-0000-1000-8000-0026BB765291", service: Service.Doorbell, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ProgrammableSwitchEvent, defaultValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Brightness, CMD4_ACC_TYPE_ENUM.Mute, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.Volume, CMD4_ACC_TYPE_ENUM.OperatingStateResponse ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.ProgrammableSwitchEvent ] }, 18: { deviceName:'Fan', deprecated: false, UUID: "00000040-0000-1000-8000-0026BB765291", service: Service.Fan, defaultCategory: Categories.FAN, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.On, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.RotationDirection, CMD4_ACC_TYPE_ENUM.RotationSpeed, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.On ] }, 19: { deviceName:'Fanv1', deprecated: false, UUID: "00000040-0000-1000-8000-0026BB765291", service: Service.Fan, defaultCategory: Categories.FAN, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.On, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.RotationDirection, CMD4_ACC_TYPE_ENUM.RotationSpeed, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.On ] }, 20: { deviceName:'Fanv2', deprecated: false, UUID: "000000B7-0000-1000-8000-0026BB765291", service: Service.Fanv2, defaultCategory: Categories.FAN, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentFanState, CMD4_ACC_TYPE_ENUM.TargetFanState, CMD4_ACC_TYPE_ENUM.LockPhysicalControls, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.RotationDirection, CMD4_ACC_TYPE_ENUM.RotationSpeed, CMD4_ACC_TYPE_ENUM.SwingMode ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 21: { deviceName:'Faucet', deprecated: false, UUID: "000000D7-0000-1000-8000-0026BB765291", service: Service.Faucet, defaultCategory: Categories.FAUCET, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.StatusFault ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 22: { deviceName:'FilterMaintenance', deprecated: false, UUID: "000000BA-0000-1000-8000-0026BB765291", service: Service.FilterMaintenance, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.FilterChangeIndication, defaultValue: Characteristic.FilterChangeIndication.FILTER_OK, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.FilterLifeLevel, CMD4_ACC_TYPE_ENUM.ResetFilterIndication, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.FilterChangeIndication ] }, 23: { deviceName:'GarageDoorOpener', deprecated: false, UUID: "00000041-0000-1000-8000-0026BB765291", service: Service.GarageDoorOpener, defaultCategory: Categories.GARAGE_DOOR_OPENER, publishExternally: false, devicesStateChangeDefaultTime: constants.SLOW_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentDoorState, defaultValue: Characteristic.CurrentDoorState.OPEN, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetDoorState ] }, {type: CMD4_ACC_TYPE_ENUM.TargetDoorState, defaultValue: Characteristic.TargetDoorState.OPEN, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentDoorState ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ObstructionDetected, defaultValue: 1, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.LockCurrentState, CMD4_ACC_TYPE_ENUM.LockTargetState, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentDoorState, CMD4_ACC_TYPE_ENUM.TargetDoorState ] }, 24: { deviceName:'HeaterCooler', deprecated: false, UUID: "000000BC-0000-1000-8000-0026BB765291", service: Service.HeaterCooler, defaultCategory: Categories.AIR_HEATER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] }, {type: CMD4_ACC_TYPE_ENUM.CurrentHeaterCoolerState, defaultValue: Characteristic.CurrentHeaterCoolerState.INACTIVE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetHeaterCoolerState ] }, {type: CMD4_ACC_TYPE_ENUM.TargetHeaterCoolerState, defaultValue: Characteristic.TargetHeaterCoolerState.HEAT, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentHeaterCoolerState ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.CurrentTemperature, defaultValue: 22.2, // Range: 0 - 100, Step: 0.1 // Format: float // Units: CELSIUS relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.LockPhysicalControls, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.RotationSpeed, CMD4_ACC_TYPE_ENUM.SwingMode, CMD4_ACC_TYPE_ENUM.CoolingThresholdTemperature, CMD4_ACC_TYPE_ENUM.HeatingThresholdTemperature, CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 25: { deviceName:'HumidifierDehumidifier', deprecated: false, UUID: "000000BD-0000-1000-8000-0026BB765291", service: Service.HumidifierDehumidifier, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentRelativeHumidity, defaultValue: 60, // Range: 0 - 100, Step: 1 // Format: float // Units: CELSIUS relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.CurrentHumidifierDehumidifierState, defaultValue: Characteristic.CurrentHumidifierDehumidifierState.IDLE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetHumidifierDehumidifierState ] }, {type: CMD4_ACC_TYPE_ENUM.TargetHumidifierDehumidifierState, defaultValue: Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentHumidifierDehumidifierState ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.LockPhysicalControls, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.RelativeHumidityDehumidifierThreshold, CMD4_ACC_TYPE_ENUM.RelativeHumidityHumidifierThreshold, CMD4_ACC_TYPE_ENUM.RotationSpeed, CMD4_ACC_TYPE_ENUM.SwingMode, CMD4_ACC_TYPE_ENUM.WaterLevel ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 26: { deviceName:'HumiditySensor', deprecated: false, UUID: "00000082-0000-1000-8000-0026BB765291", service: Service.HumiditySensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentRelativeHumidity, defaultValue: 60, // Range: 0 - 100, Step: 1 // Format: float // Units: CELSIUS relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.StatusLowBattery ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentRelativeHumidity ] }, 27: { deviceName:'InputSource', deprecated: false, UUID: "000000D9-0000-1000-8000-0026BB765291", service: Service.InputSource, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ConfiguredName, defaultValue: "My_InputSource", // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.InputSourceType, defaultValue: Characteristic.InputSourceType.HOME_SCREEN, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.IsConfigured, defaultValue: Characteristic.IsConfigured.CONFIGURED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.CurrentVisibilityState, defaultValue: Characteristic.CurrentVisibilityState.SHOWN, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Identifier, CMD4_ACC_TYPE_ENUM.InputDeviceType, CMD4_ACC_TYPE_ENUM.TargetVisibilityState, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentVisibilityState ] }, 28: { deviceName:'IrrigationSystem', deprecated: false, UUID: "000000CF-0000-1000-8000-0026BB765291", service: Service.IrrigationSystem, defaultCategory: Categories.SPRINKLER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] }, {type: CMD4_ACC_TYPE_ENUM.ProgramMode, defaultValue: Characteristic.ProgramMode.NO_PROGRAM_SCHEDULED , relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.InUse, defaultValue: Characteristic.InUse.IN_USE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.RemainingDuration, CMD4_ACC_TYPE_ENUM.StatusFault ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 29: { deviceName:'LeakSensor', deprecated: false, UUID: "00000083-0000-1000-8000-0026BB765291", service: Service.LeakSensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.LeakDetected, defaultValue: Characteristic.LeakDetected.LEAK_NOT_DETECTED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.LeakDetected ] }, 30: { deviceName:'LightSensor', deprecated: false, UUID: "00000084-0000-1000-8000-0026BB765291", service: Service.LightSensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentAmbientLightLevel, defaultValue: 1, // Range: 0.0001 - 100000 // Format: float // Units: lux relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentAmbientLightLevel ] }, 31: { deviceName:'Lightbulb', deprecated: false, UUID: "00000043-0000-1000-8000-0026BB765291", service: Service.Lightbulb, defaultCategory: Categories.LIGHTBULB, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.On, defaultValue: 0, // Format: float relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Brightness, CMD4_ACC_TYPE_ENUM.Hue, CMD4_ACC_TYPE_ENUM.Saturation, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.ColorTemperature ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.On ] }, 32: { deviceName:'LockManagement', deprecated: false, UUID: "00000044-0000-1000-8000-0026BB765291", service: Service.LockManagement, defaultCategory: Categories.ALARM_SYSTEM, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.LockControlPoint, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Version, defaultValue: '0.0.0', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.AdministratorOnlyAccess, CMD4_ACC_TYPE_ENUM.AudioFeedback, CMD4_ACC_TYPE_ENUM.CurrentDoorState, CMD4_ACC_TYPE_ENUM.LockManagementAutoSecurityTimeout, CMD4_ACC_TYPE_ENUM.LockLastKnownAction, CMD4_ACC_TYPE_ENUM.Logs, CMD4_ACC_TYPE_ENUM.MotionDetected, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ ] }, 33: { deviceName:'LockMechanism', deprecated: false, UUID: "00000045-0000-1000-8000-0026BB765291", service: Service.LockMechanism, defaultCategory: Categories.DOOR_LOCK, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.LockCurrentState, defaultValue: Characteristic.LockCurrentState.UNSECURED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.LockTargetState ] }, {type: CMD4_ACC_TYPE_ENUM.LockTargetState, defaultValue: Characteristic.LockTargetState.UNSECURED, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.LockCurrentState ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.LockCurrentState, CMD4_ACC_TYPE_ENUM.LockTargetState ] }, 34: { deviceName:'Microphone', deprecated: false, UUID: "00000112-0000-1000-8000-0026BB765291", service: Service.Microphone, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Mute, defaultValue: 0, // Format: Bool, // 0 - Mute is off // 1 - Mute is on relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Volume, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Mute ] }, 35: { deviceName:'MotionSensor', deprecated: false, UUID: "00000085-0000-1000-8000-0026BB765291", service: Service.MotionSensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.MotionDetected, defaultValue: 1, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.StatusTampered ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.MotionDetected ] }, 36: { deviceName:'OccupancySensor', deprecated: false, UUID: "00000086-0000-1000-8000-0026BB765291", service: Service.OccupancySensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.OccupancyDetected, defaultValue: Characteristic.OccupancyDetected.OCCUPANCY_NOT_DETECTED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusTampered, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.OccupancyDetected ] }, 37: { deviceName:'Outlet', deprecated: false, UUID: "00000047-0000-1000-8000-0026BB765291", service: Service.Outlet, defaultCategory: Categories.OUTLET, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.On, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.OutletInUse, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.On ] }, 38: { deviceName:'Pairing', deprecated: false, UUID: "00000055-0000-1000-8000-0026BB765291", service: Service.Pairing, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ListPairings, defaultValue: 1, // Format: Uint8. Values ??? relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.PairSetup, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.PairVerify, defaultValue: 0, // Format: Uint8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.PairingFeatures, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.PairingFeatures ] }, 39: { deviceName:'PowerManagement', deprecated: false, UUID: "00000221-0000-1000-8000-0026BB765291", service: Service.PowerManagement, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.WakeConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [] }, 40: { deviceName:'ProtocolInformation', deprecated: false, UUID: "000000A2-0000-1000-8000-0026BB765291", service: Service.ProtocolInformation, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Version, defaultValue: '1.2.3', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Version ] }, 41: { deviceName:'Relay', // Use CloudRelay deprecated: true, UUID: "0000005A-0000-1000-8000-0026BB765291", //service: Service.CloudRelay, service: null , defaultCategory: Categories.SWITCH, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ //{type: CMD4_ACC_TYPE_ENUM.RelayEnabled, // defaultValue: 1, // Format: Bool // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.RelayState, // defaultValue: 1, // Format: uint8, Values ??? // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.RelayControlPoint, // defaultValue: 0, // Format: TLV8 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //} ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [ //CMD4_ACC_TYPE_ENUM.RelayEnabled, //CMD4_ACC_TYPE_ENUM.RelayState, ] }, 42: { deviceName:'SecuritySystem', deprecated: false, UUID: "0000007E-0000-1000-8000-0026BB765291", service: Service.SecuritySystem, defaultCategory: Categories.SECURITY_SYSTEM, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SecuritySystemCurrentState, defaultValue: Characteristic.SecuritySystemCurrentState.DISARMED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.SecuritySystemTargetState ] }, {type: CMD4_ACC_TYPE_ENUM.SecuritySystemTargetState, defaultValue: Characteristic.SecuritySystemTargetState.DISARM, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.SecuritySystemCurrentState ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.SecuritySystemAlarmType, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusTampered ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.SecuritySystemCurrentState, CMD4_ACC_TYPE_ENUM.SecuritySystemTargetState ] }, 43: { deviceName:'ServiceLabel', deprecated: false, UUID: "000000CC-0000-1000-8000-0026BB765291", service: Service.ServiceLabel, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ServiceLabelNamespace, defaultValue: Characteristic.ServiceLabelNamespace.DOTS, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.ServiceLabelNamespace ] }, 44: { deviceName:'Siri', deprecated: false, UUID: "00000133-0000-1000-8000-0026BB765291", service: Service.Siri, defaultCategory: Categories.HOMEPOD, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SiriInputType, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.MultifunctionButton, CMD4_ACC_TYPE_ENUM.SiriEnable, CMD4_ACC_TYPE_ENUM.SiriEngineVersion, CMD4_ACC_TYPE_ENUM.SiriLightOnUse, CMD4_ACC_TYPE_ENUM.SiriListening, CMD4_ACC_TYPE_ENUM.SiriTouchToUse ], defaultPollingCharacteristics: [] }, 45: { deviceName:'Slats', deprecated: false, UUID: "000000B9-0000-1000-8000-0026BB765291", service: Service.Slats, defaultCategory: Categories.WINDOW_COVERING, publishExternally: false, devicesStateChangeDefaultTimeb: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentSlatState, defaultValue: Characteristic.CurrentSlatState.FIXED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SlatType, defaultValue: Characteristic.SlatType.HORIZONTAL, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.SwingMode, CMD4_ACC_TYPE_ENUM.CurrentTiltAngle, CMD4_ACC_TYPE_ENUM.TargetTiltAngle ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentSlatState ] }, 46: { deviceName:'SmartSpeaker', deprecated: false, UUID: "00000228-0000-1000-8000-0026BB765291", service: Service.SmartSpeaker, defaultCategory: Categories.HOMEPOD, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentMediaState, defaultValue: Characteristic.CurrentMediaState.STOP, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetMediaState ] }, {type: CMD4_ACC_TYPE_ENUM.TargetMediaState, defaultValue: Characteristic.CurrentMediaState.STOP, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentMediaState ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.AirPlayEnable, CMD4_ACC_TYPE_ENUM.ConfiguredName, CMD4_ACC_TYPE_ENUM.Mute, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.Volume ], defaultPollingCharacteristics: [] }, 47: { deviceName:'SmokeSensor', deprecated: false, UUID: "00000087-0000-1000-8000-0026BB765291", service: Service.SmokeSensor, defaultCategory: Categories.SENSOR, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SmokeDetected, defaultValue: Characteristic.SmokeDetected.SMOKE_NOT_DETECTED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.StatusTampered ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.SmokeDetected ] }, 48: { deviceName:'Speaker', deprecated: false, UUID: "00000113-0000-1000-8000-0026BB765291", service: Service.Speaker, defaultCategory: Categories.SPEAKER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Mute, defaultValue: 0, // Format: Bool // 0 - Mute is off // 1 - Mute is on relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active, CMD4_ACC_TYPE_ENUM.Volume, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Mute ] }, 49: { deviceName:'StatefulProgrammableSwitch', deprecated: false, UUID: "00000088-0000-1000-8000-0026BB765291", service: Service.StatefulProgrammableSwitch, defaultCategory: Categories.SWITCH, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ProgrammableSwitchEvent, defaultValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ProgrammableSwitchOutputState, defaultValue: 0, // Range: 0 - 1. Step: 1 // Format: Uint8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.ProgrammableSwitchEvent ] }, 50: { deviceName:'StatelessProgrammableSwitch', deprecated: false, UUID: "00000089-0000-1000-8000-0026BB765291", service: Service.StatelessProgrammableSwitch, defaultCategory: Categories.SWITCH, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ProgrammableSwitchEvent, defaultValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.ServiceLabelIndex ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.ProgrammableSwitchEvent ] }, 51: { deviceName:'Switch', deprecated: false, UUID: "00000049-0000-1000-8000-0026BB765291", service: Service.Switch, defaultCategory: Categories.SWITCH, publishExternally: false, devicesStateChangeDefaultTime: constants.FAST_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.On, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.On ] }, 52: { deviceName:'TargetControl', deprecated: false, UUID: "00000125-0000-1000-8000-0026BB765291", service: Service.TargetControl, defaultCategory: Categories.TARGET_CONTROLLER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] }, {type: CMD4_ACC_TYPE_ENUM.ActiveIdentifier, defaultValue: 7, // Format: UINT32 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ButtonEvent, defaultValue: 0, // TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [] }, 53: { deviceName:'TargetControlManagement', deprecated: false, UUID: "00000122-0000-1000-8000-0026BB765291", service: Service.TargetControlManagement, defaultCategory: Categories.TARGET_CONTROLLER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.TargetControlSupportedConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TargetControlList, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [] }, 54: { deviceName:'Television', deprecated: false, UUID: "000000D8-0000-1000-8000-0026BB765291", service: Service.Television, defaultCategory: Categories.TELEVISION, publishExternally: true, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] }, {type: CMD4_ACC_TYPE_ENUM.ActiveIdentifier, defaultValue: 123, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, // Format: Uint32 {type: CMD4_ACC_TYPE_ENUM.ConfiguredName, defaultValue: 'My_Tv', relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, // Format: String {type: CMD4_ACC_TYPE_ENUM.RemoteKey, defaultValue: Characteristic.RemoteKey.SELECT, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, // Format: uint8 {type: CMD4_ACC_TYPE_ENUM.SleepDiscoveryMode, defaultValue: Characteristic.SleepDiscoveryMode.ALWAYS_DISCOVERABLE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Brightness, CMD4_ACC_TYPE_ENUM.ClosedCaptions, CMD4_ACC_TYPE_ENUM.DisplayOrder, CMD4_ACC_TYPE_ENUM.CurrentMediaState, CMD4_ACC_TYPE_ENUM.TargetMediaState, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.PictureMode, CMD4_ACC_TYPE_ENUM.PowerModeSelection ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 55: { deviceName:'TelevisionSpeaker', deprecated: false, UUID: "00000113-0000-1000-8000-0026BB765291", service: Service.TelevisionSpeaker, defaultCategory: Categories.AUDIO_RECEIVER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Mute, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active, CMD4_ACC_TYPE_ENUM.Volume, CMD4_ACC_TYPE_ENUM.VolumeControlType, CMD4_ACC_TYPE_ENUM.VolumeSelector, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Mute ] }, 56: { deviceName:'TemperatureSensor', deprecated: false, UUID: "0000008A-0000-1000-8000-0026BB765291", service: Service.TemperatureSensor, defaultCategory: Categories.SWITCH, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentTemperature, defaultValue: 50.0, // Range: 0 - 100 // Step: 0.1 // Format: float // Units: CELSIUS relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.StatusActive, CMD4_ACC_TYPE_ENUM.StatusFault, CMD4_ACC_TYPE_ENUM.StatusLowBattery, CMD4_ACC_TYPE_ENUM.StatusTampered ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentTemperature ] }, 57: { deviceName:'Thermostat', deprecated: false, UUID: "0000004A-0000-1000-8000-0026BB765291", service: Service.Thermostat, defaultCategory: Categories.THERMOSTAT, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentHeatingCoolingState, defaultValue: Characteristic.CurrentHeatingCoolingState.OFF, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetHeatingCoolingState ] }, {type: CMD4_ACC_TYPE_ENUM.TargetHeatingCoolingState, defaultValue: Characteristic.TargetHeatingCoolingState.OFF, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentHeatingCoolingState ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.CurrentTemperature, defaultValue: 50.0, // Range: 0 - 100, Step: 0.1 // Format: float // Units: CELSIUS relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetTemperature ] }, {type: CMD4_ACC_TYPE_ENUM.TargetTemperature, defaultValue: 50.0, // Range: 0 - 100 // Step: 0.1 // Format: float // Units: CELSIUS relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentTemperature ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits, defaultValue: Characteristic.TemperatureDisplayUnits.CELSIUS, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.CurrentRelativeHumidity, CMD4_ACC_TYPE_ENUM.TargetRelativeHumidity, CMD4_ACC_TYPE_ENUM.CoolingThresholdTemperature, CMD4_ACC_TYPE_ENUM.HeatingThresholdTemperature ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentTemperature, CMD4_ACC_TYPE_ENUM.TargetTemperature ] }, // @deprecated Removed and not used anymore as of homebridge v2 58: { deviceName:'TimeInformation', deprecated: true, UUID: "00000099-0000-1000-8000-0026BB765291", //service: Service.TimeInformation, service: null, //defaultCategory: Categories.OTHER, defaultCategory: null, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ //{type: CMD4_ACC_TYPE_ENUM.CurrentTime, // defaultValue: '11:15', // Format: String // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.DayoftheWeek, // defaultValue: 1, // Range: 1 - 7 // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //}, //{type: CMD4_ACC_TYPE_ENUM.TimeUpdate, // defaultValue: 0, // Format: Bool // relatedCurrentAccTypeEnumArray: [ ], // relatedTargetAccTypeEnumArray: [ ] //} ], optionalCharacteristics: [ //CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [ //CMD4_ACC_TYPE_ENUM.CurrentTime ] }, 59: { deviceName:'TransferTransportManagement', deprecated: false, UUID: "00000203-0000-1000-8000-0026BB765291", service: Service.TransferTransportManagement, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SupportedTransferTransportConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SetupTransferTransport, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [] }, 60: { deviceName:'Tunnel', deprecated: false, UUID: "00000056-0000-1000-8000-0026BB765291", service: Service.Tunnel, defaultCategory: Categories.OTHER, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Name, defaultValue: 'My_TunnelB', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.AccessoryIdentifier, defaultValue: 'TLB', // Format: String relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TunneledAccessoryStateNumber, defaultValue: 0.0, // Format: float relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TunneledAccessoryConnected, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TunneledAccessoryAdvertising, defaultValue: 0, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TunnelConnectionTimeout, defaultValue: 5000, // Format: Uint32 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.TunneledAccessoryConnected ] }, 61: { deviceName:'Valve', deprecated: false, UUID: "000000D0-0000-1000-8000-0026BB765291", service: Service.Valve, defaultCategory: Categories.FAUCET, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] }, {type: CMD4_ACC_TYPE_ENUM.InUse, defaultValue: Characteristic.InUse.IN_USE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ValveType, defaultValue: Characteristic.ValveType.GENERIC_VALVE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.IsConfigured, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.RemainingDuration, CMD4_ACC_TYPE_ENUM.ServiceLabelIndex, CMD4_ACC_TYPE_ENUM.SetDuration, CMD4_ACC_TYPE_ENUM.StatusFault ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 62: { deviceName:'WiFiRouter', deprecated: false, UUID: "0000020A-0000-1000-8000-0026BB765291", service: Service.WiFiRouter, defaultCategory: Categories.AIRPORT, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ConfiguredName, defaultValue: "My_WiFiRouter", relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ManagedNetworkEnable, defaultValue: 0, // DISABLE relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.NetworkAccessViolationControl, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.NetworkClientProfileControl, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.NetworkClientStatusControl, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.RouterStatus, defaultValue: 0, // READY relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedRouterConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.WANConfigurationList, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.WANStatusList, defaultValue: 0, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [] }, 63: { deviceName:'WiFiSatellite', deprecated: false, UUID: "0000020F-0000-1000-8000-0026BB765291", service: Service.WiFiSatellite, defaultCategory: Categories.TV_SET_TOP_BOX, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.WiFiSatelliteStatus, defaultValue: 2, // NOT_CONNECTED relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ // None ], defaultPollingCharacteristics: [] }, 64: { deviceName:'Window', deprecated: false, UUID: "0000008B-0000-1000-8000-0026BB765291", service: Service.Window, defaultCategory: Categories.WINDOW, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentPosition, defaultValue: 0, // Range: 0 - 100 // Step: 1 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetPosition ] }, {type: CMD4_ACC_TYPE_ENUM.PositionState, defaultValue: Characteristic.PositionState.STOPPED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TargetPosition, defaultValue: 0, // Range: 0 - 100 // Step: 1 relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentPosition ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.ObstructionDetected, CMD4_ACC_TYPE_ENUM.HoldPosition ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentPosition, CMD4_ACC_TYPE_ENUM.TargetPosition ] }, 65: { deviceName:'WindowCovering', deprecated: false, UUID: "0000008C-0000-1000-8000-0026BB765291", service: Service.WindowCovering, defaultCategory: Categories.WINDOW_COVERING, publishExternally: false, devicesStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentPosition, defaultValue: 0, // Range: 0 - 100 // Step: 1 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.TargetPosition ] }, {type: CMD4_ACC_TYPE_ENUM.PositionState, defaultValue: Characteristic.PositionState.STOPPED, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TargetPosition, defaultValue: 0, // Range: 0 - 100 // Step: 1 relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.CurrentPosition ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentHorizontalTiltAngle, CMD4_ACC_TYPE_ENUM.TargetHorizontalTiltAngle, CMD4_ACC_TYPE_ENUM.Name, CMD4_ACC_TYPE_ENUM.ObstructionDetected, CMD4_ACC_TYPE_ENUM.HoldPosition, CMD4_ACC_TYPE_ENUM.CurrentVerticalTiltAngle, CMD4_ACC_TYPE_ENUM.TargetVerticalTiltAngle ], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.CurrentPosition, CMD4_ACC_TYPE_ENUM.TargetPosition ] }, 66: { deviceName:'AccessoryMetrics', deprecated: false, UUID: "00000270-0000-1000-8000-0026BB765291", service: Service.AccessoryMetrics, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.MetricsBufferFullState, defaultValue: 1, // Format: Bool relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedMetrics, defaultValue: "", // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [ CMD4_ACC_TYPE_ENUM.Active ] }, 67: { deviceName:'AssetUpdate', deprecated: false, UUID: "00000267-0000-1000-8000-0026BB765291", service: Service.AssetUpdate, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.AssetUpdateReadiness, defaultValue: 0, // Int relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedAssetTypes, defaultValue: 0, // UINT32 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 68: { deviceName:'Assistant', deprecated: false, UUID: "0000026A-0000-1000-8000-0026BB765291", service: Service.Assistant, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Identifier, defaultValue: "Some Identifier", // Format: STRING relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Name, defaultValue: "Unnamed Assistant", relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 69: { deviceName:'AudioStreamManagement', deprecated: false, UUID: "00000127-0000-1000-8000-0026BB765291", service: Service.AudioStreamManagement, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SupportedAudioStreamConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SelectedAudioStreamConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 70: { deviceName:'Battery', deprecated: false, UUID: "00000096-0000-1000-8000-0026BB765291", service: Service.Battery, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.StatusLowBattery, defaultValue: Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.BatteryLevel, CMD4_ACC_TYPE_ENUM.ChargingState, CMD4_ACC_TYPE_ENUM.Name ], defaultPollingCharacteristics: [] }, 71: { deviceName:'CameraRecordingManagement', deprecated: false, UUID: "00000204-0000-1000-8000-0026BB765291", service: Service.CameraRecordingManagement, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedCameraRecordingConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedVideoRecordingConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedAudioRecordingConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SelectedCameraRecordingConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.RecordingAudioActive ], defaultPollingCharacteristics: [] }, 72: { deviceName:'CloudRelay', deprecated: false, UUID: "0000005A-0000-1000-8000-0026BB765291", service: Service.CloudRelay, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.RelayControlPoint, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.RelayState, defaultValue: 1, // Format: uint8, Values ??? relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.RelayEnabled, defaultValue: false, relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 73: { deviceName:'DataStreamTransportManagement', deprecated: false, UUID: "00000129-0000-1000-8000-0026BB765291", service: Service.DataStreamTransportManagement, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SetupDataStreamTransport, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.SupportedDataStreamTransportConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Version, defaultValue: "Unknown version", relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 74: { deviceName:'NFCAccess', deprecated: false, UUID: "00000266-0000-1000-8000-0026BB765291", service: Service.NFCAccess, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.ConfigurationState, defaultValue: 0, // Format: UINT16 values? relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.NFCAccessControlPoint, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.NFCAccessSupportedConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 75: { deviceName:'SiriEndpoint', deprecated: false, UUID: "00000253-0000-1000-8000-0026BB765291", service: Service.SiriEndpoint, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.SiriEndpointSessionStatus, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Version, defaultValue: "Unknown version", relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.ActiveIdentifier, CMD4_ACC_TYPE_ENUM.ManuallyDisabled ], defaultPollingCharacteristics: [] }, 76: { deviceName:'ThreadTransport', deprecated: false, UUID: "00000701-0000-1000-8000-0026BB765291", service: Service.ThreadTransport, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentTransport, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ThreadControlPoint, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ThreadNodeCapabilities, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ThreadStatus, defaultValue: 0, // min 0, max 6 values? relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [ CMD4_ACC_TYPE_ENUM.CCAEnergyDetectThreshold, CMD4_ACC_TYPE_ENUM.CCASignalDetectThreshold, CMD4_ACC_TYPE_ENUM.EventRetransmissionMaximum, CMD4_ACC_TYPE_ENUM.EventTransmissionCounters, CMD4_ACC_TYPE_ENUM.MACRetransmissionMaximum, CMD4_ACC_TYPE_ENUM.MACTransmissionCounters, CMD4_ACC_TYPE_ENUM.ReceiverSensitivity, CMD4_ACC_TYPE_ENUM.ReceivedSignalStrengthIndication, CMD4_ACC_TYPE_ENUM.SignalToNoiseRatio, CMD4_ACC_TYPE_ENUM.ThreadOpenThreadVersion, CMD4_ACC_TYPE_ENUM.TransmitPower, CMD4_ACC_TYPE_ENUM.MaximumTransmitPower ], defaultPollingCharacteristics: [] }, // New Mar 2024 77: { deviceName:'AccessCode', deprecated: false, UUID: "00000260-0000-1000-8000-0026BB765291", service: Service.AccessCode, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.AccessCodeControlPoint, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.AccessCodeSupportedConfiguration, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.ConfigurationState, defaultValue: 0, // Format: Uint16 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 78: { deviceName:'FirmwareUpdate', deprecated: false, UUID: "00000236-0000-1000-8000-0026BB765291", service: Service.FirmwareUpdate, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.FirmwareUpdateReadiness, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.FirmwareUpdateStatus, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [CMD4_ACC_TYPE_ENUM.StagedFirmwareVersion, CMD4_ACC_TYPE_ENUM.SupportedFirmwareUpdateConfiguration ], defaultPollingCharacteristics: [] }, 79: { deviceName:'TapManagement', deprecated: false, UUID: "0000022E-0000-1000-8000-0026BB765291", service: Service.TapManagement, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.Active, defaultValue: Characteristic.Active.ACTIVE, relatedCurrentAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ], relatedTargetAccTypeEnumArray: [ CMD4_ACC_TYPE_ENUM.Active ] }, {type: CMD4_ACC_TYPE_ENUM.CryptoHash, defaultValue: 0, // Format TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.TapType, defaultValue: 0, // Format UINT16 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.Token, defaultValue: "", // Format DATA relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [], defaultPollingCharacteristics: [] }, 80: { deviceName:'WiFiTransport', deprecated: false, UUID: "0000022A-0000-1000-8000-0026BB765291", service: Service.WiFiTransport, defaultCategory: Categories.OTHER, publishExternally: false, deviceStateChangeDefaultTime: constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, requiredCharacteristics: [ {type: CMD4_ACC_TYPE_ENUM.CurrentTransport, defaultValue: 0, // Format: TLV8 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] }, {type: CMD4_ACC_TYPE_ENUM.WiFiCapabilities, defaultValue: 0, // Format: UINT32 relatedCurrentAccTypeEnumArray: [ ], relatedTargetAccTypeEnumArray: [ ] } ], optionalCharacteristics: [CMD4_ACC_TYPE_ENUM.WiFiConfigurationControl], defaultPollingCharacteristics: [] } }; return CMD4_DEVICE_TYPE_ENUM; }, CMD4_DEVICE_TYPE_ENUM } ================================================ FILE: package.json ================================================ { "name": "homebridge-cmd4", "description": "Exec Plugin for Homebridge supporting all accessorys and characteristics", "version": "8.0.4", "license": "MIT", "author": { "name": "John Talbot" }, "repository": { "type": "git", "url": "git://github.com/ztalbot2000/homebridge-cmd4.git" }, "homepage": "https://github.com/ztalbot2000/homebridge-cmd4#readme", "changelog": "https://github.com/ztalbot2000/homebridge-cmd4/blob/master/ChangeLog.md", "bugs": { "url": "https://github.com/ztalbot2000/homebridge-cmd4/issues", "email": "ztalbot2000@gmail.com" }, "dependencies": { "chalk": "^4.1.0", "command-exists": "^1.2.9", "fakegato-history": ">=0.6.5", "latest-version": "^5.1.0", "moment": "*" }, "devDependencies": { "@commitlint/cli": "^11.0.0", "@commitlint/config-conventional": "^11.0.0", "chai": "^4.2.0", "commander": "^8.2.0", "commitlint-plugin-function-rules": "^1.1.20", "eslint": "^7.17.0", "generate-changelog": "^1.8.0", "husky": "^4.3.8", "link-checker": "^1.4.2", "markdown-link-check": "^3.8.6", "mocha": "^8.0.1", "node-persist": "^0.0.11", "sinon": "^9.2.4", "which": "^2.0.2" }, "directories": { "test": "test", "lib": "lib", "utils": "utils", "docs": "docs" }, "engines": { "node": "^18.20.4 || ^20.18.0 || ^22.10.0 || ^24.0.0", "homebridge": "^1.8.0 || ^2.0.0-beta.0" }, "keywords": [ "homebridge-plugin", "homebridge", "exec", "command", "switch", "light", "door", "thermostat", "security", "temperature", "virtual", "Eve", "Homekit", "siri", "home", "Eve" ], "husky": { "hooks": { "pre-commit": "npm run lint && npm run test test/cmd4Constants", "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }, "scripts": { "debug": "DEBUG=* homebridge --debug --plugin-path .", "pretest": "printf ' test is only done in a development environment\n';sleep 2 ", "testDebug": "node_modules/.bin/mocha --require async-dump.js --file test/mocha-setup --ignore test/versionChecker.js", "test": "node_modules/.bin/mocha --file test/mocha-setup --ignore test/versionChecker.js", "singleTest": "node_modules/.bin/mocha --bail --file test/mocha-setup --ignore test/versionChecker.js", "allTests": "node_modules/.bin/mocha --bail --file test/mocha-setup $( cat test/allTests )", "sanityTests": "node_modules/.bin/mocha --bail --file test/mocha-setup $( cat test/sanityTests )", "_Note0": "Excluded from commit as may fail for probing GitHub to much", "testMarkDown": "markdown-link-check ./README.md && markdown-link-check ./CHANGELOG.md && markdown-link-check ./docs/AdvancedTroubleShooting.md && markdown-link-check ./docs/Developers.md", "testAutoGeneratedDocLinks": "link-checker docs/autoGenerated/CMD4_AccessoryDescriptions.html", "commitTests": "npm run test && npm run testMarkDown", "_Note": "Excluded only because they sometimes fail, but pass alone. ", "testCmd4Accessory": "node_modules/.bin/mocha --file test/mocha-setup test/Cmd4Accessory.js", "testVersionChecker": "node_modules/.bin/mocha --file test/mocha-setup test/versionChecker.js", "lint": "eslint --ext .js *.js utils/*.js lib/*.js tools/* test/*.js", "postinstall": "node postinstall.js", "_Note01": "Auto change the version based on the commit history. ", "_Note02": "Where: ", "_Note03": "To trigger a version update ", "_Note04": " npm run release ", "_Note05": " ", "_Note06": "To trigger a patch update (1.0.0 → 1.0.1) ", "_Note07": " git commit -m 'fix: …' ", "_Note08": "To trigger a minor update (1.0.0 → 1.1.0) ", "_Note09": " git commit -m 'feat: …' ", "_Note10": "To trigger a major update (1.0.0 → 2.0.0) ", "_Note11": " A BREAKING CHANGE: in the commit body ", "_Note12": " ", "_Note13": "As a summary, generate-changelog will do ", "_Note14": " * Bumps the version in package.json ", "_Note15": " * Updates CHANGELOG.md ", "_Note16": " ", "release": "tools/generateChangeLog", "_Note17": " ", "_Note18": "Overide default behaviour with: ", "release:patch": "tools/generateChangeLog --type patch --cleanup", "release:minor": "tools/generateChangeLog --type minor --cleanup", "release:major": "tools/generateChangeLog --type major --cleanup" } } ================================================ FILE: postinstall.js ================================================ // Post install notes // Fun colour stuff const chalk = require( "chalk" ); const myPkg = require( "./package.json" ); const { isUpgrade } = require( "./utils/versionChecker" ); // To use await you must be in an async function, so put it in one. ( async( ) => { // Wait for the Promise of isUpgrade to complete. let lv = await isUpgrade( ); if ( lv == true ) { console.log( chalk.green( `[UPDATE AVAILABLE] ` ) + `Version ${lv} of ${myPkg.name} is available. Any release notes can be found here: ` + chalk.underline( `${myPkg.changelog}` ) ); } console.log( chalk.yellow( `HomeBridge-Cmd4 5.0.0+ Important Notice:\n` ) ); console.log( `Cmd4 has been optimized for simplification and best practices. Its configuration has changed to what is recommended by Homebridge. See https://git.io/JtMGR.\n` ); console.log( `Gone are the are the very confusing Cmd4_Mode and RestartRecovery. The only changes you will see are the warnings that these options are no longer required.` ); console.log( chalk.red( `* ` ) + `RestartRecovery is now automatic; which not enabling could cause your device to turn on/off over a restart.` ); console.log( chalk.red( `* ` ) + `Cmd4_Mode is as per https://git.io/JtMGR where the callback is immediate to homebridge with the data from your device to follow.` ); console.log( chalk.red( `* ` ) + `Demo mode is still available by not defining any polling.\n` ); console.log( chalk.underline( `Cmd4 New Users` ) ); console.log( chalk.green( `* ` ) + `You will need to follow the README to continue the configuration of HomeBridge-CMD4.\n` ); console.log(`\n As always, if you like this plugin, don't forget to star it on GitHub.\n`); console.log(`\n Enjoy`); console.log(` John Talbot\n`); })( ); ================================================ FILE: test/CMD4_ACC_TYPE_ENUM.js ================================================ 'use strict'; var _api = new HomebridgeAPI(); // object we feed to Plugins var Service = _api.hap.Service; describe( `Testing require of CMD4_ACC_TYPE_ENUM.js`, ( ) => { it( `CMD4_ACC_TYPE_ENUM should be defined ( required correctly )`, ( ) => { assert.isNotNull( ACC_DATA, `CMD4_ACC_TYPE_ENUM is null` ); }); it( `ACC_DATA.init should be a function`, ( ) => { assert.isFunction( ACC_DATA.init, `.init is not a function` ); }); // ************ TEST UNINITIALIZED CMD4_ACC_TYPE_ENUM EOL ************** describe( `Testing CMD4_ACC_TYPE_ENUM.EOL`, ( ) => { it( `CMD4_ACC_TYPE_ENUM has EOL`, ( ) => { assert.isNotNull( CMD4_ACC_TYPE_ENUM.EOL, `EOL is null` ); }); it( `CMD4_ACC_TYPE_ENUM.EOL = ${ ACC_EOL }`, ( ) => { assert.equal( CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, `CMD4_ACC_TYPE_ENUM.EOL FOUND: ${ CMD4_ACC_TYPE_ENUM.EOL }` ); }); it( `CMD4_ACC_TYPE_ENUM[ 0-${ CMD4_ACC_TYPE_ENUM.EOL } ] should equal value at index`, ( ) => { for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ ) { assert.notEqual( CMD4_ACC_TYPE_ENUM[index], index ); } }); }); }) describe( `Testing INITIALIZED CMD4_ACC_TYPE_ENUM`, ( ) => { // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); describe(`Testing Initialized CMD4_ACC_TYPE_ENUM.properties[]`, ( ) => { it('CMD4_ACC_TYPE_ENUM.properties should be an object', ( ) => { assert.isObject(CMD4_ACC_TYPE_ENUM.properties, `CMD4_DEVICE_TYPE_ENUM.properties is not an object` ); }); it(`Testing CMD4_ACC_TYPE_ENUM.properties[]`, ( ) => { for (let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ ) { let result = CMD4_ACC_TYPE_ENUM.properties[ index ]; // Make sure our properties are defined assert.isNotNull( result, `CMD4_ACC_TYPE_ENUM.properties[${ index }] is null. result: ${ result }` ); for (let jIndex=0; jIndex < CMD4_ACC_TYPE_ENUM.EOL; jIndex ++ ) { if ( index == jIndex) continue; it( `CMD4_ACC_TYPE_ENUM.properties[ ${ index } ].type should not be duplicated`, ( ) => { assert.equal( CMD4_ACC_TYPE_ENUM.properties[ index ].type, CMD4_ACC_TYPE_ENUM.properties[ jIndex ].type, ` Duplicate ACC type ${ CMD4_ACC_TYPE_ENUM.properties[ index ].type }` ); }); } } }); }); // TJOS STIFF ADDED // ** TEST ACC_TYPE_ENUM.properties[].props ** describe('Testing CMD4_ACC_TYPE_ENUM.properties[].props', ( ) => { for ( let accTypeEnumIndex=0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex++ ) { if ( accTypeEnumIndex == CMD4_ACC_TYPE_ENUM.Name ) continue; // Do not understand why Charateristic.PairingPairings is // undefined, but is okay in Cmd4 // Problem occured with hap-nodejs 0.9.2, but not 0.8.5 && // homebridge 1.3.1, but not 1.1.7 if ( accTypeEnumIndex == CMD4_ACC_TYPE_ENUM.PairingPairings ) continue; if ( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].deprecated == true ) continue; it( `CMD4_ACC_TYPE_ENUM.properties[ ${ accTypeEnumIndex } ].type should be a string`, ( ) => { assert.isString( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type, ` type is not a string at index ${ accTypeEnumIndex }` ); }); let characteristic = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].characteristic; // *** TEST CMD4_ACC_TYPE_ENUM.properties[].characteristic ******* // Make sure our characteristic is defined assert.isNotNull( characteristic, `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].characteristic is null` ); assert.isFunction( characteristic, `characteristic is not a function at accTypeEnumIndex: ${ accTypeEnumIndex } result: ${ characteristic }` ); it(`Testing CMD4_ACC_TYPE_ENUM.properties[].UUID is same as in hap string `, ( ) => { // Make sure that the UUID we defined is valid. assert.equal( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].UUID, characteristic.UUID, ` Our UUID is not the same at index: ${ accTypeEnumIndex }` ); }); it(`Testing CMD4_ACC_TYPE_ENUM.properties[].description is a string `, ( ) => { assert.isString( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].description, ` description is not a string at index: ${ accTypeEnumIndex }` ); }); it(`Testing CMD4_ACC_TYPE_ENUM.properties[].description String length > 10 `, ( ) => { assert.isAtLeast( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].description.length, 10, ` description is is very short at index: ${ accTypeEnumIndex }` ); }); // Get the properties for this characteristic type let accProperties = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex]; // Characteristics dont seem to get removed and homebridge put a limit // of 100 Characteristics per service, so just create a new service // per characteristic. This is unit testing anyway, so not an issue. let serviceName = `Service${ accTypeEnumIndex }`; let service = new _api.hap.Service(serviceName, _api.hap.uuid.generate(serviceName ), serviceName ); it('Creating service to test Characteristic', ( ) => { assert.isNotNull(service, `Service is null at accTypeEnumIndex: ${ accTypeEnumIndex }` ); }); describe(`Testing CMD4_ACC_TYPE_ENUM.properties[ ${ accTypeEnumIndex } ]: ${ accProperties.type }`, ( ) => { var characteristicString = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].type; // Check relatedCurrentAccTypeEnumIndex property it( `CMD4_ACC_TYPE_ENUM.properties[ ${ accTypeEnumIndex } ].relatedCurrentAccTypeEnumIndex`, ( ) => { assert.isDefined( CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].relatedCurrentAccTypeEnumIndex, 'relatedCurrentAccTypeEnumIndex is not defined' ); let relatedCurrentAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].relatedCurrentAccTypeEnumIndex; if ( relatedCurrentAccTypeEnumIndex != null ) { assert.isNumber( relatedCurrentAccTypeEnumIndex, `${ characteristicString } relatedCurrentAccTypeEnumIndex is not a number: ${ typeof relatedCurrentAccTypeEnumIndex }` ); assert.isAbove( relatedCurrentAccTypeEnumIndex, 0, `${ characteristicString } relatedCurrentAccTypeEnumIndex < 0: ${ relatedCurrentAccTypeEnumIndex }` ); assert.isBelow( relatedCurrentAccTypeEnumIndex, ACC_EOL, `${ characteristicString } relatedCurrentAccTypeEnumIndex is < ACC_EOL: ${ relatedCurrentAccTypeEnumIndex }` ); // Make sure they point to each other. assert.equal( accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.properties[relatedCurrentAccTypeEnumIndex].relatedTargetAccTypeEnumIndex, `${ characteristicString } relatedCurrentAccTypeEnumIndex (${ relatedCurrentAccTypeEnumIndex }) not matching releatedTargetAccTypeEnumIndex` ); } }); // Check relatedTargetAccTypeEnumIndex property it( `CMD4_ACC_TYPE_ENUM.properties[ ${ accTypeEnumIndex } ].relatedTargetAccTypeEnumIndex`, ( ) => { assert.isDefined( CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].relatedTargetAccTypeEnumIndex, 'relatedTargetAccTypeEnumIndex is not defined' ); let relatedTargetAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].relatedTargetAccTypeEnumIndex; if ( relatedTargetAccTypeEnumIndex != null ) { assert.isNumber( relatedTargetAccTypeEnumIndex, `${ characteristicString } relatedTargetAccTypeEnumIndex is not a number: ${ typeof relatedTargetAccTypeEnumIndex }` ); assert.isAbove( relatedTargetAccTypeEnumIndex, 0, `${ characteristicString } relatedTargetAccTypeEnumIndex < 0: ${ relatedTargetAccTypeEnumIndex }` ); assert.isBelow( relatedTargetAccTypeEnumIndex, ACC_EOL, `${ characteristicString } relatedTargetAccTypeEnumIndex is < ACC_EOL: ${ relatedTargetAccTypeEnumIndex }` ); // Make sure they point to each other. assert.equal( accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.properties[relatedTargetAccTypeEnumIndex].relatedCurrentAccTypeEnumIndex, `${ characteristicString } relatedTargetAccTypeEnumIndex (${ relatedTargetAccTypeEnumIndex }) not matching relatedCurrentAccTypeEnumIndex` ); } }); // We need to add the characteristic to the service so we can get its Hap properties service.addCharacteristic( characteristic ); let hapProps = service.getCharacteristic(characteristic ).props; service.removeCharacteristic( characteristic ); it( `HAP props for HomeBridge Characteristic: ${ accProperties.type }`, ( ) => { assert.isNotNull( hapProps, `perms for Characteristic type: ${ accProperties.type } is null` ); }); let hapFormat = hapProps.format; // Test both Formats it(`CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].props.format should be the same`, ( ) => { assert.equal( hapFormat, accProperties.props.format, `format:${ accProperties.props.format } not equal to expected ${ hapFormat }` ); }); // Test cFormat it(`CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].props.cFormat should not be null`, ( ) => { assert.isNumber( accProperties.props.allowedWordCount, `allowedWordCount is not a Number at accTypeEnumIndex:${ accTypeEnumIndex }` ); switch( hapFormat ) { case _api.hap.Formats.STRING: case _api.hap.Formats.TLV8: case _api.hap.Formats.DATA: assert.equal( 0, accProperties.props.allowedWordCount, `allowedWordCount not 0 at accTypeEnumIndex: ${ accTypeEnumIndex }` ); break; case _api.hap.Formats.INT: case _api.hap.Formats.UINT8: case _api.hap.Formats.UINT16: case _api.hap.Formats.UINT32: case _api.hap.Formats.BOOL: case _api.hap.Formats.FLOAT: assert.equal( 1, accProperties.props.allowedWordCount, `allowedWordCount not 1 at accTypeEnumIndex: ${ accTypeEnumIndex }` ); break; default: assert( `allowedWordCount unknown at accTypeEnumIndex: ${ accTypeEnumIndex }` ); } }); // Test units let hapUnits = hapProps.units; it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].props.units should be the same`, ( ) => { assert.equal(hapUnits, accProperties.props.units, `units: ${ accProperties.props.units } not equal to expected: ${ hapUnits }` ); }); // test minValue let hapMinValue = hapProps.minValue; it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].props.minValue should be the same`, ( ) => { assert.equal( hapMinValue, accProperties.props.minValue, `minValue: ${ accProperties.props.minValue } not equal to expected: ${ hapMinValue }` ); }); // test maxValue let hapMaxValue = hapProps.maxValue; // They are missing AUTO if ( accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.CurrentHeatingCoolingState ) { it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].props.maxValue should be the same`, ( ) => { assert.equal( hapMaxValue, accProperties.props.maxValue, `maxValue: ${ accProperties.props.maxValue } not equal to expected: ${ hapMaxValue }` ); }); } else { //console.log(`Homebridge is wrong, skipping` ); } // test minStep let hapMinStep = hapProps.minStep; it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].props.minStep should be the same`, ( ) => { assert.equal( hapMinStep, accProperties.props.minStep, `minStep: ${ accProperties.props.minStep } not equal to expected: ${ hapMinStep }` ); }); // Test if correct perms ( both ways ) it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].props.perms are the same`, ( ) => { let hapPerms = hapProps.perms; accProperties.props.perms.forEach( perm => assert.include( hapPerms, perm, ` ${ characteristicString } ( ${ accTypeEnumIndex } ) missing perm: ${ perm } in Hap Perm` ) ); hapPerms.forEach( perm => assert.include( accProperties.props.perms, perm, ` ${ characteristicString } ( ${ accTypeEnumIndex } ) missing perm: ${ perm } in accProperties.props.perms` ) ); }); // Our validValues should not be null it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].validValues should not be null`, ( ) => { assert.isNotNull(accProperties.validValues, 'validValues is Null' ); }); // test all validValues let hapValidValues = hapProps.validValues; if ( ! hapValidValues ) { // We defined what valid values are for BOOL // so TRUE/FALSE can be a constant. if (hapFormat != _api.hap.Formats.BOOL ) { // Hap has values defined, but not in valid values. We added them in ours if ( accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.CameraOperatingModeIndicator && accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.ThirdPartyCameraActive && accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.RecordingAudioActive && accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.PeriodicSnapshotsActive && accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.HomeKitCameraActive && accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.EventSnapshotsActive ) { // Test if our validValues is empty it( `Our CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].validValues.length should be 0`, ( ) => { assert.equal(Object.keys(accProperties.validValues ).length, 0, `validValuesh for: ${ accTypeEnumIndex } is not empty` ); }); } else { // console.log(`Homebridge is wrong, skipping` ); } } } else { // For InputDeviceType, we define the UNKNOWN_6 they do not. its okay // For ManagedNetworkEnable, we define the UNKNOWN they do not. its okay if ( accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.CurrentHeatingCoolingState && accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.InputDeviceType && accTypeEnumIndex != CMD4_ACC_TYPE_ENUM.ManagedNetworkEnable ) { // Test if same number of validValues it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].perms.length should be the same`, ( ) => { assert.equal( hapValidValues.length, Object.keys(accProperties.validValues ).length, `validValues.length(${ Object.keys(accProperties.validValues ).length }) for: ${ accTypeEnumIndex } is not: ${ hapValidValues.length }` ); }); // test all validValues against hapValidValues for ( let valuesIndex = 0; valuesIndex < hapValidValues.length; valuesIndex++ ) { let hapValue = hapValidValues[valuesIndex]; it( `CMD4_ACC_TYPE_ENUM.properties[${ accTypeEnumIndex }].validValues[${ valuesIndex }] should be the same`, ( ) => { let cmd4Key = Object.keys(accProperties.validValues )[valuesIndex]; let cmd4Value = accProperties.validValues[cmd4Key]; assert.equal( hapValue, cmd4Value, `validValues[${ valuesIndex }]: ${ cmd4Value } not equal to expected: ${ hapValue }` ); }); } } else { // console.log(`Homebridge is wrong, skipping` ); } } service.removeCharacteristic(characteristic ); }); } }); }); describe( `Testing CMD4_ACC_TYPE_ENUM.indexOfEnum`, ( ) => { // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); it('CMD4_ACC_TYPE_ENUM.indexOfEnum should be a function', ( ) => { assert.isFunction(CMD4_ACC_TYPE_ENUM.indexOfEnum, `CMD4_DEVICE_TYPE_ENUM.indexOfEnum is not a function` ); }); it(`Testing CMD4_ACC_TYPE_ENUM.indexOfEnum( "On" )`, ( ) => { let result = CMD4_ACC_TYPE_ENUM.indexOfEnum( "On" ); assert.equal( result, CMD4_ACC_TYPE_ENUM.On,`Index is incorrect for "On"` ); }); it(`Testing CMD4_ACC_TYPE_ENUM.indexOfEnum( "on" )`, ( ) => { let result = CMD4_ACC_TYPE_ENUM.indexOfEnum( "on" ); assert.equal( result, CMD4_ACC_TYPE_ENUM.On,`Index is incorrect for "on"` ); }); }); describe( `Testing CMD4_ACC_TYPE_ENUM stringConversionFunction`, ( ) => { // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); describe( `Testing CMD4_ACC_TYPE_ENUM.properties[].stringConversionFunction`, ( ) => { it( 'CMD4_ACC_TYPE_ENUM[ 0 - ${ ACC_EOL } ].stringConversionFunction should be a function', ( ) => { for ( let index = 0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ ) { assert.isFunction( CMD4_ACC_TYPE_ENUM.properties[ index ].stringConversionFunction, `Missing conversion function at index: ${ index }` ); } }); }); it(`stringConversionFunction of AirParticulateSize of UINT8 Number should be Number.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.AirParticulateSize; let value = 60; let expectedResult = 60; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of AirParticulateSize of UINT8 String should be Number.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.AirParticulateSize; let value = "60"; let expectedResult = 60; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of TargetTemperature of Float should be Float.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; let value = 60.2; let expectedResult = 60.2; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of TargetTemperature of Float String should be Float.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; let value = "60.2"; let expectedResult = 60.2; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of TargetTemperature of Float "0.0" should be 0.0`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; let value = "0.0"; let expectedResult = 0.0; let expectedResultType = "number"; // There is no float type let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); let resultType = typeof result; assert.equal( resultType, expectedResultType, `stringConversionFunction of valid data with full properties returned incorrect result. Expected: ${ expectedResultType } received: ${ result }` ); }); it(`stringConversionFunction of TargetTemperature of Float "0" should be float`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; let value = "0"; let expectedResult = 0; let expectedResultType = "number"; // There is no float type let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); let resultType = typeof result; assert.equal( resultType, expectedResultType, `stringConversionFunction of valid data with full properties returned incorrect result. Expected: ${ expectedResultType } received: ${ result }` ); }); it(`stringConversionFunction of Name of string1 should be String.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Name; let value = "Device"; let expectedResult = "Device"; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Name of number should be String.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Name; let value = 123; let expectedResult = "123"; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of FAlse should be false.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = "FAlse"; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of "FAlse" should be false.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = "FAlse"; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of "True" should be true.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = "true"; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of false should be false.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = false; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of true should be true.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = true; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of 0 should be false.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = 0; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of 1 should be true (On).`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = 1; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of BOOL of 0 should be false (Off) .`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.On; let value = 0; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of BOOL of 1 should be True (On).`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.On; let value = 1; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of 1 should be true .`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = 1; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of "1" should be True (On).`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = "1"; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of 0 should be false (off).`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = 0; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of "0" should be false.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = "0"; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of true should be true.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = true; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of false should be false.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = false; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of "false" should be false.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = "false"; let expectedResult = false; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); it(`stringConversionFunction of Mute of "true" should be true.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Mute; let value = "true"; let expectedResult = true; let result = CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].stringConversionFunction( value ); assert.equal( result, expectedResult, `stringConversionFunction of valid data with full properties returned incorrect result: ${ result }` ); }); }); describe( `Testing CMD4_ACC_TYPE_ENUM Add Characteristic`, ( ) => { // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); it( `ACC_DATA.add should be a function`, ( ) => { assert.isFunction( CMD4_ACC_TYPE_ENUM.add, `.add is not a function` ); }); it( `CMD4_ACC_TYPE_ENUM.EOL = ${ ACC_EOL }`, ( ) => { assert.equal( CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, `CMD4_ACC_TYPE_ENUM.EOL FOUND: ${ CMD4_ACC_TYPE_ENUM.EOL }` ); }); // Changing the number of characteristics screws up other tests. it.skip( `CMD4_ACC_TYPE_ENUM.add creates new characteristic, incrementing EOL`, ( ) => { let definition = { }; definition.type = "PointX"; definition.description = "An X Coordinate"; definition.props = { }; definition.props.format = "uint32"; definition.props.minValue = 0; definition.props.minStep = 1; definition.props.perms = [ "pr", "pw", "ev" ]; definition.validValues = { }; let characteristic = CMD4_ACC_TYPE_ENUM.add( _api, definition.type, definition.description, definition.props, definition.validValues ); let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.PointX; assert.equal( accTypeEnumIndex, ACC_EOL, `ACC_TYPE_ENUM_INDEX.PointX not defined` ); assert.equal( CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL +1, `CMD4_ACC_TYPE_ENUM not incremented` ); assert.isNotNull( characteristic, `New characteristic was is null ` ); }); // Changing the number of characteristics screws up other tests. it.skip( `CMD4_ACC_TYPE_ENUM.add creates new characteristic compatible with service`, ( ) => { let definition = { }; definition.type = "PointX"; definition.description = "An X Coordinate"; definition.props = { }; definition.props.format = "uint32"; definition.props.minValue = 0; definition.props.minStep = 1; definition.props.perms = [ "pr", "pw", "ev" ]; definition.validValues = { }; // Create the new characteristic. CMD4_ACC_TYPE_ENUM.add( _api, definition.type, definition.description, definition.props, definition.validValues ); // Create a service to add the new characteristic. let switchService = new Service.Switch(); assert.instanceOf( switchService , Service, "Expected switchService to be instance of Service. Found %s" , switchService ); // Add the new characteristic to the test service. switchService.addCharacteristic( CMD4_ACC_TYPE_ENUM.properties[CMD4_ACC_TYPE_ENUM.PointX].characteristic ); // Check to see if the characteristic was accepted by the service let result = switchService.testCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.PointX ].characteristic ); assert.isTrue( result, `New characteristic not added to service. result: ${ result }` ); }); }); ================================================ FILE: test/CMD4_CHAR_TYPE_ENUMS.js ================================================ 'use strict'; var _api = new HomebridgeAPI(); // object we feed to Plugins let { indexOfEnum } = require( "../utils/indexOfEnum" ); Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } }); describe( "Testing require of CMD4_CHAR_TYPE_ENUMS.js", ( ) => { it( "CMD4_CHAR_TYPE_ENUMS should be defined ( required correctly )", ( ) => { assert.isNotNull( CMD4_CHAR_TYPE_ENUMS, "CMD4_CHAR_TYPE_ENUMS is null" ); }); // ************ TEST UNINITIALIZED CMD4_FORMAT_TYPE_ENUM EOL ************** describe( "Testing CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL", ( ) => { it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM has EOL", ( ) => { assert.isNotNull( CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL, "EOL is null" ); }); it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL = " + FORMAT_EOL, ( ) => { assert.equal( CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL, FORMAT_EOL, "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL FOUND: " + CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL ); }); it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM[ 0-" + CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL + " ] should equal value at index", ( ) => { for ( let index=0; index < CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL; index ++ ) { assert.notEqual( CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM[index], index ); } }); }); // ************ TEST UNINITIALIZED CMD4_UNITS_TYPE_ENUM EOL ************** describe( "Testing CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL", ( ) => { it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM has EOL", ( ) => { assert.isNotNull( CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL, "EOL is null" ); }); it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL = " + UNITS_EOL, ( ) => { assert.equal( CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL, UNITS_EOL, "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL FOUND: " + CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL ); }); it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM[ 0-" + CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL + " ] should equal value at index", ( ) => { for ( let index=0; index < CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL; index ++ ) { assert.notEqual( CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM[index], index ); } }); }); // ************ TEST UNINITIALIZED CMD4_PERMS_TYPE_ENUM EOL ************** describe( "Testing CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL", ( ) => { it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM has EOL", ( ) => { assert.isNotNull( CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL, "EOL is null" ); }); it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL = " + PERMS_EOL, ( ) => { assert.equal( CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL, PERMS_EOL, "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL FOUND: " + CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL ); }); it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM[ 0-" + CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL + " ] should equal value at index", ( ) => { for ( let index=0; index < CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL; index ++ ) { assert.notEqual( CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM[index], index ); } }); }); }) describe( "Testing INITIALIZED CMD4_CHAR_TYPE_ENUMS", ( ) => { // Init the library for all to use let CMD4_CHAR_TYPE_ENUMS = CHAR_DATA.init( _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); describe("Testing Initialized CMD4_CHAR_TYPE_ENUMS.", ( ) => { // Test a format type it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.float should equal expected value", ( ) => { let formatIndex = CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.properties.indexOfEnum( i => i.type === "float" ); assert.equal( formatIndex, 2, `CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM "float" not equal to expected index`); }); // Test a units type it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.lux should equal expected value", ( ) => { let unitsIndex = CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.properties.indexOfEnum( i => i.type === "lux" ); assert.equal( unitsIndex, 3, `CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM "lux" not equal to expected index`); }); // Test a perms type it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.ev should equal expected value", ( ) => { let permsIndex = CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.properties.indexOfEnum( i => i.type === "ev" ); assert.equal( permsIndex, 4, `CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM "ev" not equal to expected index`); }); }); }); ================================================ FILE: test/CMD4_DEVICE_TYPE_ENUM.js ================================================ 'use strict'; var _api = new HomebridgeAPI(); // object we feed to Plugins describe( "Testing require of CMD4_DEVICE_TYPE_ENUM.js", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM should be defined ( required correctly)", ( ) => { assert.isNotNull( DEVICE_DATA, "CMD4_DEVICE_TYPE_ENUM is null" ); }); it( "DEVICE_DATA.init should be a function", ( ) => { assert.isFunction( DEVICE_DATA.init, ".init is not a function" ); }); // ************ TEST UNINITIALIZED CMD4_DEVICE_TYPE_ENUM EOL ************** describe( "Testing CMD4_DEVICE_TYPE_ENUM.EOL", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM has EOL", ( ) => { assert.isNotNull( CMD4_DEVICE_TYPE_ENUM.EOL, "EOL is null" ); }); it( "CMD4_DEVICE_TYPE_ENUM.EOL = " + DEVICE_EOL, ( ) => { assert.equal( CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL, "CMD4_DEVICE_TYPE_ENUM.EOL FOUND: " + CMD4_DEVICE_TYPE_ENUM.EOL ); }); it( "CMD4_DEVICE_TYPE_ENUM[ 0-" + CMD4_DEVICE_TYPE_ENUM.EOL + " ] should equal value at index", ( ) => { for ( let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { assert.notEqual( CMD4_DEVICE_TYPE_ENUM[index], index ); } }); }); }) // ************ TEST INITIALIZED CMD4_DEVICE_TYPE_ENUM ************** describe( "Testing INITIALIZED CMD4_DEVICE_TYPE_ENUM", ( ) => { // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // *** TEST CMD4_DEVICE_TYPE_ENUM.properties[].UUID ******* describe('Testing Initialized CMD4_DEVICE_TYPE_ENUM.properties[].UUID', ( ) => { it('Testing CMD4_DEVICE_TYPE_ENUM.properties[].UUID is a string ', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; let result = CMD4_DEVICE_TYPE_ENUM.properties[ index ].UUID; // Make sure that the UUID we defined is valid. expect(result).to.be.a( "string", " result is not a string. result:" + result + " at index: " + index ); } }); it('Testing CMD4_DEVICE_TYPE_ENUM.properties[].UUID is same as in hap string ', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; let service = CMD4_DEVICE_TYPE_ENUM.properties[ index ].service; assert.isNotNull( service, ` CMD4_DEVICE_TYPE_ENUM.properties[${ index } ].service is null` ); let hapUUID = service.UUID; let result = CMD4_DEVICE_TYPE_ENUM.properties[ index ].UUID; // Make sure that the UUID we defined is valid. expect(result).to.equal( hapUUID, " Our UUID is not the same at index: " + index + ". result:" + result ); } }); }); // *** TEST CMD4_DEVICE_TYPE_ENUM.properties[].service ******* describe('Testing Initialized CMD4_DEVICE_TYPE_ENUM.properties[].service', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].service ', ( ) => { // Make sure our service is defined let service = CMD4_DEVICE_TYPE_ENUM.properties[ index ].service; assert.isNotNull( service, "CMD4_DEVICE_TYPE_ENUM.properties[" + index + "].service is null:" ); }); } }); // *** TEST CMD4_DEVICE_TYPE_ENUM.properties[].defaultCategory ******* describe('Testing Initialized CMD4_DEVICE_TYPE_ENUM.properties[].defaultCategory', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].defaultCategory ', ( ) => { // Make sure our defaultCategory is defined let defaultCategory = CMD4_DEVICE_TYPE_ENUM.properties[ index ].defaultCategory; assert.isNumber(defaultCategory, "CMD4_DEVICE_TYPE_ENUM.properties[" + index + "].defaultCategory is not a number. category:" + defaultCategory ); let result = _api.hap.Categories[ defaultCategory ]; // Make sure that the default category we defined is valid. assert.isString(result, "CMD4_DEVICE_TYPE_ENUM.properties[" + index + "].defaultCategory is not a string. result:" + result ); }); } }); // *** TEST CMD4_DEVICE_TYPE_ENUM.properties[].publishExternally ******* describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[].publishExternally', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].publishExternally ', ( ) => { assert.isBoolean(CMD4_DEVICE_TYPE_ENUM.properties[ index ].publishExternally, "CMD4_DEVICE_TYPE_ENUM.properties[" + index + "].publishExternally is not a true|false" ); }); } }); describe('Testing Initialized CMD4_DEVICE_TYPE_ENUM.properties[].defaultCategory', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].defaultCategory ', ( ) => { // Make sure our defaultCategory is defined let defaultCategory = CMD4_DEVICE_TYPE_ENUM.properties[ index ].defaultCategory; assert.isNumber(defaultCategory, "CMD4_DEVICE_TYPE_ENUM.properties[" + index + "].defaultCategory is not a number. category:" + defaultCategory ); let result = _api.hap.Categories[ defaultCategory ]; // Make sure that the default category we defined is valid. assert.isString(result, "CMD4_DEVICE_TYPE_ENUM.properties[" + index + "].defaultCategory is not a string. result:" + result ); }); } }); // *** TEST CMD4_DEVICE_TYPE_ENUM.properties[].publishExternally ******* describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[].publishExternally', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].publishExternally ', ( ) => { assert.isBoolean(CMD4_DEVICE_TYPE_ENUM.properties[ index ].publishExternally, "CMD4_DEVICE_TYPE_ENUM.properties[" + index + "].publishExternally is not a true|false" ); }); } }); // ******** TEST CMD4_DEVICE_TYPE_ENUM.properties ************* describe('Testing CMD4_DEVICE_TYPE_ENUM.properties', ( ) => { it('CMD4_DEVICE_TYPE_ENUM.properties should be an array', ( ) => { assert.isObject(CMD4_DEVICE_TYPE_ENUM.properties, "CMD4_DEVICE_TYPE_ENUM.properties is not an object" ); }); for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '] should not be null ', ( ) => { assert.isNotNull(CMD4_DEVICE_TYPE_ENUM.properties[index], 'properties[' + index + '] is null' ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].deviceName should not be null ', ( ) => { assert.isNotNull(CMD4_DEVICE_TYPE_ENUM.properties[index].deviceName, 'deviceName is null at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].service should not be null ', ( ) => { assert.isNotNull(CMD4_DEVICE_TYPE_ENUM.properties[index].service, 'service is null at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].defaultCategory should not be null ', ( ) => { assert.isNotNull(CMD4_DEVICE_TYPE_ENUM.properties[index].defaultCategory, 'defaultCategory is null at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].publishExternally should not be null ', ( ) => { assert.isNotNull(CMD4_DEVICE_TYPE_ENUM.properties[index].publishExternally, 'publishExternally is null at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].devicesStateChangeDefaultTime should not be null ', ( ) => { assert.isNotNull(CMD4_DEVICE_TYPE_ENUM.properties[index].devicesStateChangeDefaultTime, 'devicesStateChangeDefaultTime is null at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].requiredCharacteristics should be an array ', ( ) => { assert.isArray(CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics, 'requiredCharacteristics is not an array at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].defaultPollingCharacteristics should be an array ', ( ) => { assert.isArray(CMD4_DEVICE_TYPE_ENUM.properties[index].defaultPollingCharacteristics, 'defaultPollingCharacteristics is not an array at index:' + index ); }); } }); // *** TEST CMD4_DEVICE_TYPE_ENUM.properties[].requiredCharacteristics ******* describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[].requiredCharacteristics', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '] ', ( ) => { let length = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics.length; for (let rindex=0; rindex < length; rindex ++ ) { describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].requiredCharacteristics[' + rindex + ']', ( ) => { let allRequiredCharacteristics = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics; let accTypeEnumIndex = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics[rindex].type; let defaultValue = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics[rindex].defaultValue; let relatedCurrentAccTypeEnumArray = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics[rindex].relatedCurrentAccTypeEnumArray; let relatedTargetAccTypeEnumArray = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics[rindex].relatedTargetAccTypeEnumArray; testCharacteristicIndex( accTypeEnumIndex ); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].requiredCharacteristics[' + rindex + '].defaultValue must not be null', ( ) => { assert.isNotNull( defaultValue, 'defaultValue is null at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].requiredCharacteristics[' + rindex + '].relatedCurrentAccTypeEnumArray must be an Array', ( ) => { assert.isArray( relatedCurrentAccTypeEnumArray, 'relatedCurrentAccTypeEnumArray is not an Array at index:' + index ); }); it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].requiredCharacteristics[' + rindex + '].relatedTargetAccTypeEnumArray must be an Array', ( ) => { assert.isArray( relatedTargetAccTypeEnumArray, 'relatedTargetAccTypeEnumArray is nat an Array at index:' + index ); }); if ( relatedCurrentAccTypeEnumArray.length > 0 ) { relatedCurrentAccTypeEnumArray.forEach( ( relatedCurrentAccTypeEnumIndex ) => { it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].requiredCharacteristics[' + rindex + '].relatedTargetAccTypeEnumArray must have Current Defined', ( ) => { console.log("looking for %s at index: %s rindex: %s", relatedCurrentAccTypeEnumIndex, index, rindex ); let foundEntry = allRequiredCharacteristics.find( entry => entry.type == relatedCurrentAccTypeEnumIndex ); //console.log("accTypeEnumIndex = %s", accTypeEnumIndex ); //console.log("relatedCurrentAccTypeEnumIndex = %s", relatedCurrentAccTypeEnumIndex ); //console.log("foundEntry = %s", foundEntry ); //console.log("relatedTargetAccTypeEnumArray = %s", relatedTargetAccTypeEnumArray ); assert.isNotNull( foundEntry, ' No relatedCurrentAccTypeEnumIndex is null at index:' + index ); let foundRelatedEntry = foundEntry.relatedTargetAccTypeEnumArray.includes( accTypeEnumIndex ); //console.log("foundRelatedEntry = %s", foundRelatedEntry ); assert.isTrue( foundRelatedEntry, ' Current pointers do not match at index:' + index ); }); }); } if ( relatedTargetAccTypeEnumArray.length > 0 ) { relatedTargetAccTypeEnumArray.forEach ( ( relatedTargetAccTypeEnumIndex ) => { it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].requiredCharacteristics[' + rindex + '].relatedCurrentAccTypeEnumIndex must have Current Defined', ( ) => { let foundEntry = allRequiredCharacteristics.find( entry => entry.type == relatedTargetAccTypeEnumIndex ); assert.isNotNull( foundEntry, ' No relatedTargetAccTypeEnumIndex is null at index:' + index ); let foundRelated = foundEntry.relatedCurrentAccTypeEnumArray.includes( accTypeEnumIndex ); assert.isTrue( foundRelated, ' Current Target do not match at index:' + index ); }); }); } }); } }); } }); // *** TEST CMD4_DEVICE_TYPE_ENUM.properties[].optionalCharacteristics ******* describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[].optionalCharacteristics', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].optionalCharacteristics ', ( ) => { let length = CMD4_DEVICE_TYPE_ENUM.properties[index].optionalCharacteristics.length; for (let rindex=0; rindex < length; rindex ++ ) { let accTypeEnumIndex = CMD4_DEVICE_TYPE_ENUM.properties[index].optionalCharacteristics[rindex]; testCharacteristicIndex(accTypeEnumIndex, rindex ); } }); } }); // ** TEST CMD4_DEVICE_TYPE_ENUM.properties[].defaultPollingCharacteristic ** describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[].defaultPollingCharacteristics', ( ) => { for (let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++ ) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; describe('Testing CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].defaultPollingCharacteristics ', ( ) => { let length = CMD4_DEVICE_TYPE_ENUM.properties[index].defaultPollingCharacteristics.length; for (let rindex=0; rindex < length; rindex ++ ) { let defaultPollingAccTypeEnumIndex = CMD4_DEVICE_TYPE_ENUM.properties[index].defaultPollingCharacteristics[rindex]; testCharacteristicIndex(defaultPollingAccTypeEnumIndex, rindex ); // Check that polled characteristic is in required characteristics let foundInRequiredCharacteristics = false; let dLength = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics.length; for (let dindex=0; dindex < dLength; dindex ++ ) { let rCharacteristic = CMD4_DEVICE_TYPE_ENUM.properties[index].requiredCharacteristics[dindex].type; if (defaultPollingAccTypeEnumIndex == rCharacteristic ) { foundInRequiredCharacteristics = true; break; } } it('CMD4_DEVICE_TYPE_ENUM.properties[' + index + '].defaultPollingCharacteristics[' + rindex + '] is in requiredCharacteristics', ( ) => { assert.isTrue(foundInRequiredCharacteristics, 'Polling characteristic is not in required Characteristics at accTypeEnumIndex:' + index ); }); } }); } }); }); function testCharacteristicIndex ( accTypeEnumIndex, rindex ) { describe('Testing accTypeEnumIndex:' + accTypeEnumIndex, () => { it('rindex should be valid', ( ) => { assert.isNotNull(rindex, `rindex must not be null accTypeEnumIndex: ${accTypeEnumIndex}` ); }); it('accTypeEnumIndex should be valid', ( ) => { assert.isNotNull(accTypeEnumIndex, `accTypeEnumIndex must not be null rindex: ${rindex}` ); }); // AccessoryFlags is enum 0. However node.js inerpets 0 as false // So if it is a number or a bool, then it is okay. it('accTypeEnumIndex is a number', ( ) => { assert.isFalse(typeof accTypeEnumIndex != 'boolean' && typeof accTypeEnumIndex != 'number', 'accTypeEnumIndex is not a number' ); }); // Test the range it('accTypeEnumIndexs is within range', ( ) => { assert.isTrue(0 <= accTypeEnumIndex && accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL, 'accTypeEnumIndex is not >= 0 && < ' + CMD4_ACC_TYPE_ENUM.EOL + ' accTypeEnumIndex:' + accTypeEnumIndex ); }); }); } ================================================ FILE: test/Cmd4Accessory.js ================================================ "use strict"; // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); const constants = require( "../cmd4Constants" ); var _api = new HomebridgeAPI(); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); var Cmd4Accessory = require( "../Cmd4Accessory" ).Cmd4Accessory; let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; let Cmd4Storage = require( "../utils/Cmd4Storage" ); // Unfortunately this test never exits, because polling will start. // Warn the user of such // Note: Not true anymore as polling moved to Platform. //function abort() //{ // console.log("Test of Cmd4Accessory requires CTRL-c as polling was backgrounded"); // setTimeout( abort, 1800 ); //} //setTimeout( ( ) => { abort(); }, 1800 ); // The State_cmd is called from $HOME const home = require( "os" ).homedir(); // THIS IS WHAT SCREWS UP THE THE UNIT TEST CASES IN Cmd4AccessoryGetValues! !!! //process.chdir( home ); // // ******** QUICK TEST of SETUP ************* describe('Quick Test of Setup', ( ) => { it('Plugin Characteristic should be a function', ( ) => { assert.isFunction(_api.hap.Characteristic, `Characteristic is not an function` ); }); it('Plugin Accessory should be a function', ( ) => { assert.isFunction(_api.hap.Accessory, `Accessory is not an function` ); }); it('Plugin Service should be a function', ( ) => { assert.isFunction(_api.hap.Service, `_api.hap.Service is not an function` ); }); it( `CMD4_ACC_TYPE_ENUM.EOL = ${ ACC_EOL }`, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); it( `CMD4_DEVICE_TYPE_ENUM.EOL = ${ DEVICE_EOL }`, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }) // ******** TEST Cmd4Accessory ************* describe('A simple Cmd4Accessory Test', ( ) => { it( `Test can create an instance of Cmd4Accessory`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.instanceOf( accessory , Cmd4Accessory, `Expected accessory to be instance of Cmd4Accessory.` ); }); }); describe('A simple Cmd4Accessory Test Debbuging enabled', ( ) => { it( `Test can create an instance of Cmd4Accessory with Debug Enabled`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.instanceOf( accessory , Cmd4Accessory, `Expected accessory to be instance of Cmd4Accessory.` ); }); }); describe('Test Cmd4Accessory variables ', ( ) => { it( `Test typeIndex of a Switch set correctly`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.instanceOf( accessory , Cmd4Accessory, `Expected accessory to be instance of Cmd4Accessory.` ); assert.equal( accessory.typeIndex , CMD4_DEVICE_TYPE_ENUM.Switch, "Expected typeIndex: %s Found: %s" , CMD4_DEVICE_TYPE_ENUM.Switch, accessory.typeIndex ); assert.equal( accessory.typeIndex , CMD4_DEVICE_TYPE_ENUM.Switch, `Unexpected matching typeIndex` ); }); it( `Test typeIndex of all possible devices`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let parentInfo = undefined; for ( let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++) { if ( CMD4_DEVICE_TYPE_ENUM.properties[ index ].deprecated == true ) continue; // Cannot create an accessory information of an accessory information if ( index == CMD4_DEVICE_TYPE_ENUM.AccessoryInformation ) { continue; } //config.name = `MY_${ CMD4_DEVICE_TYPE_ENUM.properties[index].deviceName }`; config.name = devEnumIndexToC( index ); config.type = CMD4_DEVICE_TYPE_ENUM.properties[index].deviceName; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.instanceOf( accessory , Cmd4Accessory, `Expected accessory to be instance of Cmd4Accessory using index ${ index}` ); assert.equal( accessory.typeIndex , index, `Unexpected typeIndex: at ${ index }` ); } }); }); describe('Cmd4Accessory Test get/test/set storedValues', ( ) => { it( `Check that STORED_DATA_ARRAY is created`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.isArray( accessory.STORED_DATA_ARRAY, `Expected accessory.STORED_DATA_ARRAY to be an Array. Found ${ typeof accessory.STORED_DATA_ARRAY }` ); }); it( `Check that Array STORED_DATA_ARRAY.cmd4Storage is created`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.instanceOf( accessory.STORED_DATA_ARRAY[0].cmd4Storage, Cmd4Storage, `Expected accessory.STORED_DATA_ARRAY[0].cmd4Storage to be an instance of Cmd4Storage. Found ${ typeof accessory.STORED_DATA_ARRAY[0].cmd4Storage }` ); }); it( `Check that cmd4Storage Array size is: ${ ACC_EOL }`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.equal( Object.keys(accessory.STORED_DATA_ARRAY[0].cmd4Storage.DATA).length, ACC_EOL, `Expected cmd4Storage to be of size: ${ ACC_EOL }` ); }); it( "Check that getStoredValueForCharacteristic UC is set correctly for a Switch", ( ) => { let config = { displayName: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let characteristicString; let cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ] ); for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++) { if ( CMD4_ACC_TYPE_ENUM.properties[ index ].deprecated == true ) continue; characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( index ); let value = cmd4Accessory.cmd4Storage.getStoredValueForCharacteristic( characteristicString ); if ( index == CMD4_ACC_TYPE_ENUM.On ) { assert.isFalse( value, `Expected accessory.getStoredValueForCharacteristic( ${ characteristicString } ) to be False using index ${ index}` ); } else { assert.isNull( value, `Expected accessory.getStoredValueForCharacteristic( ${ characteristicString } ) to be null using index ${ index}` ); } } }); it( "Check that getStoredValueForCharacteristic LC is set correctly for a Switch", ( ) => { let config = { displayName: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let characteristicString; let cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ] ); for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++) { if ( CMD4_ACC_TYPE_ENUM.properties[ index ].deprecated == true ) continue; characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( index ); let value = cmd4Accessory.cmd4Storage.getStoredValueForCharacteristic( characteristicString ); if ( index == CMD4_ACC_TYPE_ENUM.On ) { assert.isFalse( value, `Expected accessory.getStoredValueForCharacteristic( ${ characteristicString } ) to be False using index ${ index}` ); } else { assert.isNull( value, `Expected accessory.getStoredValueForCharacteristic( ${ characteristicString } ) to be null using index ${ index}` ); } } }); it( "Check that getStoredValueForIndex is set correctly for a Switch", ( ) => { let config = { displayName: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ] ); for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++) { if ( CMD4_ACC_TYPE_ENUM.properties[ index ].deprecated == true ) continue; let value = cmd4Accessory.cmd4Storage.getStoredValueForIndex( index ); if ( index == CMD4_ACC_TYPE_ENUM.On ) { assert.isFalse( value, `Expected accessory.getStoredValueForIndex( ${ index } ) to be False using index ${ index}` ); } else { assert.isNull( value, `Expected accessory.getStoredValueForIndex( ${ index } ) to be null using index ${ index}` ); } } }); it( `Check setStoredValueForIndex throws error with accTypeEnumIndex < 0`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); let errMsg=`setStoredValue - Characteristic index: -1 not between 0 and ${ ACC_EOL }\nCheck your config.json file for unknown characteristic.`; expect ( ( ) => accessory.cmd4Storage.setStoredValueForIndex( -1, 0 ) ).to.throw(errMsg); }); it( `Check setStoredValueForIndex throws error with accTypeEnumIndex >= EOL`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); let errMsg=`setStoredValue - Characteristic index: ${ ACC_EOL } not between 0 and ${ ACC_EOL }\nCheck your config.json file for unknown characteristic.`; expect ( ( ) => accessory.cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.EOL, 0 ) ).to.throw(errMsg); }); it( `Check getStoredValue works correctly for a Switch`, ( ) => { let config = { displayName: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); let STORED_DATA_ARRAY = [ ]; let accessory = new Cmd4Accessory( log, config, _api, STORED_DATA_ARRAY ); let newValue = true; let value; let characteristicString; accessory.cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, newValue ); for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++) { if ( CMD4_ACC_TYPE_ENUM.properties[ index ].deprecated == true ) continue; characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( index ); value = STORED_DATA_ARRAY[0].cmd4Storage.DATA[ index ]; if ( index == CMD4_ACC_TYPE_ENUM.On ) assert.isTrue( value, `Expected accessory.STORED_DATA_ARRAY( ${ index } ) to be False using index ${ index}` ); else assert.isNull( value, `Expected accessory.STORED_DATA_ARRAY( ${ index } ) to be null using index ${ index}` ); } let result = accessory.cmd4Storage.getStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On ); assert.equal( result, newValue, `Unexpected getStoredValueForIndex( ${ CMD4_ACC_TYPE_ENUM.On }` ); characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); result = accessory.cmd4Storage.getStoredValueForCharacteristic( characteristicString); assert.equal( result, newValue, `Unexpected getStoredValueForCharacteristic( ${ characteristicString }` ); }); it( `Check getStoredValueForIndex throws error with accTypeEnumIndex < 0`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); let errMsg=`getStoredValue - Characteristic index: -1 not between 0 and ${ ACC_EOL }\nCheck your config.json file for unknown characteristic.`; expect ( ( ) => accessory.cmd4Storage.getStoredValueForIndex( -1 ) ).to.throw(errMsg); }); it( `Check getStoredValueForIndex throws error with accTypeEnumIndex >= EOL`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); let errMsg=`setStoredValue - Characteristic index: ${ ACC_EOL } not between 0 and ${ ACC_EOL }\nCheck your config.json file for unknown characteristic.`; expect ( ( ) => accessory.cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.EOL, 0 ) ).to.throw(errMsg); }); it( `Check testStoredValue works correctly for a Switch`, ( ) => { let config = { displayName: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); log.setBufferEnabled( ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); let characteristicString = accEnumIndexToC( CMD4_ACC_TYPE_ENUM.On ); config.On = true; let newValue = true; accessory.cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, newValue ); for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++) { if ( CMD4_ACC_TYPE_ENUM.properties[ index ].deprecated == true ) continue; characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( index ); let value = accessory.cmd4Storage.testStoredValueForIndex( index ); let cvalue = accessory.cmd4Storage.testStoredValueForCharacteristic( characteristicString ); if ( index == CMD4_ACC_TYPE_ENUM.On ) { assert.equal( value, newValue, `Unexpected return value for accessory.testStoredValueForIndex( ${ index } ) using index ${ index}` ); assert.equal( cvalue, newValue, `Unexpected return value for accessory.testStoredValueForCharacteristic( ${ characteristicString } ) using index ${ index}` ); } else { assert.isNull( value, `Expected return value for accessory.testStoredValueForIndex( ${ index } ) to be null using index ${ index}` ); assert.isNull( cvalue, `Expected return value for accessory.testStoredValueForCharacteristic( ${ characteristicString } ) to be null using index ${ index}` ); } } }); it( `Check testStoredValue limits returns undefined`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); let value, index; let accessory = new Cmd4Accessory( log, config, _api, [ ] ); // Test below zero index = -1; value = accessory.cmd4Storage.testStoredValueForIndex( index ); assert.isUndefined( value, `Expected accessory.testStoredValueForIndex( ${ index } ) to return undefind` ); // Test not given value = accessory.cmd4Storage.testStoredValueForCharacteristic( ); assert.isUndefined( value, `Expected accessory.testStoredValueForCharacteristic() to return undefined` ); // Test above EOL index = ACC_EOL + 1; value = accessory.cmd4Storage.testStoredValueForIndex( index ); assert.isUndefined( value, `Expected accessory.testStoredValueForIndex( ${ index } ) to return undefined` ); // Test not given value = accessory.cmd4Storage.testStoredValueForCharacteristic( "Does not exist" ); assert.isUndefined( value, `Expected accessory.testStoredValueForCharacteristic() to return undefined` ); }); it( `Check that Array STORED_DATA_ARRAY.cmd4Storage is created`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.instanceOf( accessory.STORED_DATA_ARRAY[0].cmd4Storage, Cmd4Storage, `Expected accessory.STORED_DATA_ARRAY[0].cmd4Storage to be an instance of Cmd4Storage. Found ${ typeof accessory.STORED_DATA_ARRAY[0].cmd4Storage }` ); }); }); describe('Cmd4Accessory Test parseConfig', ( ) => { it( "Check that invalid category throws an error", ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, category: "Bad" }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Category specified: "Bad" is not a valid homebridge category for: "Test Switch"./); }); it( `Check that queue is already defined before use throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, queue: "A" }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/"QueueType" must be defined first for queue "A" in: "Test Switch"/); }); it( `Check that type is defined for an accessory throws an error`, ( ) => { let config = { name: "Test Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Unknown device type: "undefined" given in: "Test Switch"./); }); it( `Check that unknown type is defined for an accessory throws an error`, ( ) => { let config = { name: "Test Switch", type: "Sink", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Unknown device type: "Sink" given in: "Test Switch"./); }); it( `Check that lower case type gives a warning and continues`, ( ) => { let config = { name: "Test Switch", type: "switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); log.setDebugEnabled( false ); new Cmd4Accessory( log, config, _api, [ ] ); assert.include( log.logBuf, `[34mCmd4 is running in Demo Mode for Test Switch`, `Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for Test Switch", `Incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mThe config.json Cmd4 device type: switch is lowerCase. It should be: Switch. In the near future this will be an error for homebridge-UI integration`, ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); assert.equal( 1, log.logLineCount, ` To many logged lines: ${ log.logBuf }` ); assert.equal( 1, log.errLineCount, ` To many logged lines: ${ log.errBuf }` ); }); it( `Check that upper case Cmd4 directive gives a warning and continues`, ( ) => { let config = { name: "Test Switch", DisplayName: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); log.setDebugEnabled( false ); let cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.instanceOf( cmd4Accessory , Cmd4Accessory, `Expected cmd4Accessory to be instance of Cmd4Accessory.` ); assert.include( log.logBuf, `[34mCmd4 is running in Demo Mode for Test Switch`, `Incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mThe config.json Cmd4 Directive: DisplayName is Capitalized. It should be: displayName. In the near future this will be an error for homebridge-ui integration`, ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); assert.equal( 1, log.logLineCount, ` To many logged lines: ${ log.logBuf }` ); assert.equal( 1, log.errLineCount, ` To many logged lines: ${ log.errBuf }` ); }); it( `Check that upper case Cmd4 Characteristic gives a warning and continues`, ( ) => { let config = { Name: "Test Switch", displayName: "Test Switch", type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( ); log.setDebugEnabled( false ); let cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.instanceOf( cmd4Accessory , Cmd4Accessory, `Expected cmd4Accessory to be instance of Cmd4Accessory.` ); assert.include( log.logBuf, `[34mCmd4 is running in Demo Mode for Test Switch`, `Incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mThe config.json characteristic key: Name is Capitalized. It should be: name. In the near future this will be an error for homebridge-ui integration`, ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); assert.equal( 1, log.logLineCount, ` To many logged lines: ${ log.logBuf }` ); assert.equal( 1, log.errLineCount, ` To many logged lines: ${ log.errBuf }` ); }); it( `Check that empty state_cmd for an accessory throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, state_cmd: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/No state_cmd for: "Test Switch"./); }); it( `Check that invalid characteristic for an accessory throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", Xon: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/OOPS: "Xon" not found for parsing characteristics in: "Test Switch"./); }); it( `Check that upper case characteristic for an accessory generates a warning`, ( ) => { let config = { name: "Test Switch", // Should be lower case name type: "Switch", on: false }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); log.setDebugEnabled( false ); new Cmd4Accessory( log, config, _api, [ ] ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for Test Switch", `Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); assert.equal( 1, log.logLineCount, ` To many logged lines: ${ log.logBuf }` ); assert.equal( 0, log.errLineCount, ` To many logged lines: ${ log.errBuf }` ); }); it( `Check that invalid polling type (string throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: "yes", state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Unknown type for Polling "yes" given in: "Test Switch"./); }); it( `Check that invalid polling characteristic for an accessory throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: [{ characteristic: "XOn"}], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/No such polling characteristic: "XOn" for: "Test Switch"./); }); it( `Check that polling characteristic is also defined throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: [{ characteristic: "active"}], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Polling for: "active" requested, but characteristic is not in your config.json file for: "Test Switch"./); }); it( `Check that polling characteristic is invalid throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: [{ "XOn": 15 }], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( true ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/OOPS: "XOn" not found while parsing for characteristic polling of "Test Switch". There something wrong with your config.json file?/); }); it( `Check that no polling characteristic defined throws an error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: [{ interval: 15 }], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setOutputEnabled( false ); log.setBufferEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/No characteristic found while parsing for characteristic polling of: "Test Switch". There something wrong with your config.json file?/); }); it( `Check that Constant definitions work`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1, "globalConstants": [ { "key": "${psk}", "value": "123456" }, { "key": "${ipaddress}", "value": "12Aa34bbcc" }, { "key": "${port}", "value": "8091" } ] }; new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.equal( log.logBuf, "", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it( `Check that Constant key starts with \${ works or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, constants: [ { key: "{psk}", value: "123456" } ], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Constant definition for: "{psk}" must start with "\${" for clarity./); }); it( `Check that Constant key ends with } works or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, constants: [ { key: "${psk", value: "123456" } ], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Constant definition for: "\${psk" must end with "}" for clarity./); }); it( `Check that Constant is an array/list or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, constants: { key: "${psk}", value: 1 }, state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Constants must be an array of { "key": "\${SomeKey}", "value": "some replacement string" }/ ); }); it( `Check that variable definitions work`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, variables: [ { key: "${psk}", value: "123456" }, { key: "${ipaddress}", value: "12Aa34bbcc" }, { key: "${port}", value: "8091" } ], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); new Cmd4Accessory( log, config, _api, [ ] ); assert.equal( log.logBuf, "", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it( `Check that variable key starts with \${ works or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, variables: [ { key: "{psk}", value: "123456" } ], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Variable definition for: "{psk}" must start with "\${" for clarity./); }); it( `Check that variable key ends with } works or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, variables: [ { key: "${psk", value: "123456" } ], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Variable definition for: "\${psk" must end with "}" for clarity./); }); it( `Check that Variables is an array/list or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, variables: "${psk}:1", state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/Variables must be an array of { "key": "\${SomeKey}", "value": "some replacement string" }/); }); it( `Check that url definitions work`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, url: "http:blah", state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); new Cmd4Accessory( log, config, _api, [ ] ); assert.equal( log.logBuf, "", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it( `Check that url is a string or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, url: 15, state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/url must be a string: "15"./); }); it( `Check that requires definitions work`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, requires: [ { "require": "${fs}" }, { "require": "http" } ], state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1, "globalConstants": [ { "key": "${fs}", "value": "fs" } ] }; new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.equal( log.logBuf, "", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it( `Check that requires is an array or throws error`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, requires: 15, state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js` }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect ( ( ) => new Cmd4Accessory( log, config, _api, [ ] ) ).to.throw(/requires must be an array of { "require": "some requires string" }/ ); }); }); describe('Cmd4Accessory Test determineCharacteristicsToPollOfAccessoryAndItsChildren', ( ) => { afterEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; }); it( `Check that cmd4Storage gets created`, ( done ) => { let config = { name: "Test Switch", type: "Switch", on: false, state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js`, interval: 10, // seconds stateChangeResponseTime: 1, // seconds timeout: 6000, // msec polling: true }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.instanceOf( accessory.STORED_DATA_ARRAY[0].cmd4Storage, Cmd4Storage, `Expected accessory.STORED_DATA_ARRAY[0].cmd4Storage to be an instance of Cmd4Storage. Found ${ typeof accessory.STORED_DATA_ARRAY[0].cmd4Storage }` ); done( ); }); it( `Check that storedValuesPer Array size is: ${ ACC_EOL }`, ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, state_cmd: `node ${ home }/.homebridge/Cmd4Scripts/State.js`, interval: 10, // seconds stateChangeResponseTime: 1, // seconds timeout: 6000, // msec polling: true }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.equal( Object.keys(accessory.STORED_DATA_ARRAY[0].cmd4Storage.DATA).length, ACC_EOL, `Expected accessory.STORED_DATA_ARRAY[0].cmd4Storage to be size: ${ ACC_EOL }` ); }); it('Polling complains related polling characteristic is missing', ( done ) => { let platformConfig = { accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { "characteristic": "currentPosition" }, { "characteristic": "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, `cmd4Platform is not an instance of Cmd4Platform` ); this.config = platformConfig.accessories; cmd4Platform.discoverDevices( ); assert.include( log.errBuf, `[33mWarning, With polling for "currentPosition" requested, you also must do polling of "targetPosition" or things will not function properly` , `expected stderr: ${ log.errBuf }` ); done( ); }); it( `Test Polling generates log.if related characteristic not polled also`, function( ) { let thermostatConfig = { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "Inactive", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, polling: [{characteristic: "currentTemperature", interval: 60, timeout:2000}], state_cmd: "./test/echoScripts/echo_quoted0" }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let parentInfo = { }; new Cmd4Accessory( log, thermostatConfig, _api, [ ], parentInfo ); assert.equal( log.logBuf, "", ` Cmd4Accessory Unexpected stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mWarning, With polling for "currentTemperature" requested, you also must do polling of "targetTemperature" or things will not function properly`, ` Cmd4Accessory Incorrect stderr: ${ log.errBuf }` ); }); it( `Test linked accessory gets \${IP} constant sent to it.`, ( done ) => { let platformConfig = { platform: "Cmd4", constants: [ { key: "${IP}", value: "192.168.2.65"} ], queueTypes: [ { queueType: "WoRm", queue: "A"} ], accessories: [ { type: "Thermostat", displayName: "Aircon", currentHeatingCoolingState: "OFF", targetHeatingCoolingState: "OFF", currentTemperature: 24, targetTemperature: 24, temperatureDisplayUnits: "CELSIUS", name: "Aircon", manufacturer: "Advantage Air Australia", model: "e-zone", serialNumber: "Daikin e-zone", queue: "A", polling: [ { characteristic: "currentHeatingCoolingState" }, { characteristic: "targetHeatingCoolingState" }, { characteristic: "currentTemperature" }, { characteristic: "targetTemperature" } ], state_cmd: "'/usr/local/lib/node_modules/homebridge-cmd4-advantageair/AdvAir.sh'", state_cmd_suffix: "${IP} noSensors", linkedTypes: [ { type: "Fan", displayName: "Fan Speed", on: "FALSE", rotationSpeed: 100, name: "Fan Speed", manufacturer: "Advantage Air Australia", model: "e-zone", serialNumber: "Daikin e-zone", queue: "A", polling: [ { characteristic: "on" }, { characteristic: "rotationSpeed" } ], state_cmd: "'/usr/local/lib/node_modules/homebridge-cmd4-advantageair/AdvAir.sh'", state_cmd_suffix: "fanspeed ${IP}" }] }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 2, ` Cmd4Platform did not create the cmd4Accessory` ); // The linked accessory is actually at index 0 let lightbulbAccessory = cmd4Platform.createdCmd4Accessories[0]; // The main accessory is actually at index 1 let thermostatAccessory = cmd4Platform.createdCmd4Accessories[1]; assert.include( log.logBuf, `[34mCreating Platform Accessory type for : Aircon LEVEL: 0`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Creating linked accessories for: Aircon`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[34mCreating Linked Platform Accessory type for : Fan Speed LEVEL: 1`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.equal( thermostatAccessory.displayName, "Aircon", ` Thermostat has incorrect displayName` ); assert.equal( lightbulbAccessory.displayName, "Fan Speed", ` Lightbulb has incorrect displayName` ); assert.equal( thermostatAccessory.state_cmd_suffix, " 192.168.2.65 noSensors", ` Accessory has incorrect state_cmd_suffix` ); assert.equal( lightbulbAccessory.state_cmd_suffix, " fanspeed 192.168.2.65", ` Linked accessory has incorrect state_cmd_suffix` ); done( ); }); it( `Test Polling generates log for linked accessory if related characteristic not polled also`, ( ) => { let thermostatConfig = { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "Inactive", currentTemperature: 20.0, targetTemperature: 20.0, linkedTypes: { type: "Thermostat", name: "Linked_Thermostat", displayName: "Linked_Thermostat", temperatureDisplayUnits: "CELSIUS", active: "Inactive", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, polling: [{characteristic: "currentTemperature", interval: 60, timeout:2000}], state_cmd: "./test/echoScripts/echo_quoted0" }, CurrentHeatingCoolingState: 0, TargetHeatingCoolingState: 0, polling: [{characteristic: "currentTemperature", interval: 60, timeout: 2000}, {characteristic: "targetTemperature", interval: 60, timeout: 2000}], State_cmd: "./test/echoScripts/echo_quoted0" }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1 }; new Cmd4Accessory( log, thermostatConfig, _api, [ ], parentInfo ); assert.include( log.logBuf, `Creating linked accessories for: Thermostat`, ` Cmd4Accessory output expected. received: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mWarning, With polling for "currentTemperature" requested, you also must do polling of "targetTemperature" or things will not function properly`, ` Cmd4Accessory Incorrect stderr:: ${ log.errBuf }` ); }); it( `Test Non Platform accessories throws an error (Cmd4 8.0)`, ( ) => { let platformConfig= { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "Inactive", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, polling: [{ characteristic: "currentTemperature" }, { characteristic: "targetTemperature" } ], state_cmd: "./test/echoScripts/echo_quoted0", accessories: [ { accessory: "Cmd4", statusMsg: true, type: "GarageDoorOpener", displayName: "StandaloneDoorOpener", name: "StandaloneDoorOpener", currentDoorState: "CLOSED", targetDoorState: "CLOSED", obstructionDetected: "1", polling: [ { characteristic: "currentDoorState" }, // { characteristic: "targetDoorState" }, { characteristic: "obstructionDetected" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); let errMsg=`Accessory: has been deprecated by homebridge. Only Platform confgurations are supported by Cmd4 >= 8.0 for: "StandaloneDoorOpener".`; expect ( ( ) => cmd4Platform.discoverDevices( ) ).to.throw(errMsg); }); }); describe('Test Cmd4Accessory change characteristic Props', ( ) => { it('Test successful change Props definition ', ( done ) => { let platformConfig = { accessories: [ { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", props: { CurrentTemperature: { maxValue: 100, minValue: -100, minStep: 0.1 } }, polling: [ { characteristic: "currentTemperature" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; assert.instanceOf( cmd4Accessory , Cmd4Accessory, `Expected cmd4Accessory to be instance of Cmd4Accessory.` ); assert.include( log.logBuf, `[90mOverriding characteristic CurrentTemperature props for: MyTemperatureSensor`, `Incorrect stdout: ${ log.logBuf }` ); done( ); }); }); ================================================ FILE: test/Cmd4AccessoryGetValue.js ================================================ "use strict"; // ***************** TEST LOADING ********************** let { Cmd4Platform } = require( "../Cmd4Platform" ); let { Cmd4Accessory } = require( "../Cmd4Accessory" ); //const constants = require( "../cmd4Constants" ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); describe( "Testing Cmd4Accessory", function( ) { afterEach( ( ) => { // MaxListenersExceededWarning: Possible EventEmitter memory leak detected _api.removeAllListeners(); }); it( "Test if Cmd4Accessory exists", function ( ) { expect( Cmd4Accessory ).not.to.be.a( "null", "Cmd4Accessory was null" ); }); it( "Test init Cmd4Accessory", function( ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); }); it( "Test Cmd4Accessory.queue.priorityGetValue", function( ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_ACTIVE" }] }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); }); /* Characteristic.getValue() is deprecated in V2 and Characteristic.value does not call the * get functions. it( "getValue active should inject 1 to Hombridge for ACTIVE response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_ACTIVE" }] }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Active ] .characteristic ).getValue( "active", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.equal( log.logBuf, "", ` getValue Unexpected stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 2000 ); }).timeout( 3000 ); it( "getValue Active should inject 0 to Hombridge for INACTIVE response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_INACTIVE" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Active ] .characteristic ).getValue( "active", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[90mgetValue: Active function for: MyTelevision returned: INACTIVE`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Active for: MyTelevision transposed: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` getValue unexpected stderr: ${ log.errBuf }` ); done( ); }, 1900); }); it( "getValue active should inject 0 to Hombridge for 0 response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_0" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Active ] .characteristic ).getValue( "Active", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[90mgetValue: Active function for: MyTelevision returned: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` getCachedValue unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Active should inject 1 to Hombridge for 1 response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_1" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Active ] .characteristic ).getValue( "Active", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Active function for: MyTelevision returned: 1`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Active should inject 0 to Hombridge for quoted0 response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_quoted0" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Active ] .characteristic ).getValue( "Active", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Active function for: MyTelevision returned: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Active should inject 1 to Hombridge for quoted1 response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_quoted1" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Active ] .characteristic ).getValue( "Active", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Active function for: MyTelevision returned: 1`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Mute should inject false to Hombridge for false response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", polling: true, state_cmd: "./test/echoScripts/echo_INACTIVE" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Active ] .characteristic ).getValue( "Active", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Active function for: MyTelevision returned: INACTIVE`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Active for: MyTelevision transposed: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Mute should inject true to Hombridge for true response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_true" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute function for: MyTelevision returned: true`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute for: MyTelevision transposed: 1`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Mute should inject false to Hombridge for 0 response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_0" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[90mgetValue: Mute function for: MyTelevision returned: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute function for: MyTelevision returned: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Mute should inject true to Hombridge for 1 response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_1" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute function for: MyTelevision returned: 1`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Mute should inject false to Hombridge for quotedFalse response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_quotedFALSE" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute function for: MyTelevision returned: False`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute for: MyTelevision transposed: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue Mute should inject true to Hombridge for quotedTrue response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_quotedTRUE" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute function for: MyTelevision returned: True`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: Mute for: MyTelevision transposed: 1`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue ClosedCaptions should inject 0 to Hombridge for DISABLED response", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since ClosedCaptions is optional polling: [{ characteristic: "closedCaptions" }], state_cmd: "./test/echoScripts/echo_DISABLED" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.ClosedCaptions ] .characteristic ).getValue( "ClosedCaptions", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: ClosedCaptions function for: MyTelevision returned: DISABLED`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: ClosedCaptions for: MyTelevision transposed: 0`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1500); }); //HERE it( "getValue of empty response should fail correctly", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_nothing", timeout: 500 }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[31mgetValue: Mute function for: MyTelevision returned an empty string ""`, ` getValue Incorrect stderr: ${ log.errBuf }` ); // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MyTelevision" getValue. Last error found Getting: "Mute". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( log.errLineCount, 2, `getValue: to many lines to stderr ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue of null response should fail correctly", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_null", timeout: 500 }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `getValue: "null" returned from stdout for Mute MyTelevision`, ` getValue Incorrect stderr: ${ log.errBuf }` ); // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MyTelevision" getValue. Last error found Getting: "Mute". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( log.errLineCount, 2, `getValue: to many lines to stderr ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue of echo true rc=1 response pass with error message", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_true_withRcOf1", timeout: 500 }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); // The script error assert.include( log.errBuf, `[31m\u001b[31mgetValue Mute function failed for MyTelevision cmd: ./test/echoScripts/echo_true_withRcOf1 Get 'MyTelevision' 'Mute' Failed. Error: 1`, ` getValue Incorrect stderr: ${ log.errBuf }` ); // The ( error ) returned from exec assert.include( log.errBuf, `[31mgetValue Mute function failed for MyTelevision cmd: ./test/echoScripts/echo_true_withRcOf1 Get 'MyTelevision' 'Mute' Failed. Generated Error: Error: Command failed: ./test/echoScripts/echo_true_withRcOf1 Get 'MyTelevision' 'Mute'`, ` getValue Incorrect stderr: ${ log.errBuf }` ); // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MyTelevision" getValue. Last error found Getting: "Mute". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( log.errLineCount, 3, `getValue: to many lines to stderr ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue of quoted Null should fail correctly", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_quotedNULL", timeout: 500 }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `getValue: Mute function for MyTelevision returned the string ""NULL""`, ` getValue Incorrect stderr: ${ log.errBuf }` ); // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MyTelevision" getValue. Last error found Getting: "Mute". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( log.errLineCount, 2, `getValue: to many lines to stderr ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue of quoted Nothing should fail correctly", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_quotedNothing", timeout: 500 }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `getValue: Mute function for: MyTelevision returned an empty string "" ""`, ` getValue Incorrect stdout: ${ log.errBuf }` ); // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MyTelevision" getValue. Last error found Getting: "Mute". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( log.errLineCount, 2, `getValue: to many lines to stderr ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue of Nothing to stdout and something to stderr should show error message", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/echo_errorToStderr", timeout: 500 }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, "[31mgetValue: Mute function for MyTelevision streamed to stderr: This message goes to stderr", ` getValue Incorrect stderr: ${ log.errBuf }` ); assert.include( log.errBuf, `[31mgetValue: Mute function for: MyTelevision returned an empty string ""`, ` getValue Incorrect stderr: ${ log.errBuf }` ); // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MyTelevision" getValue. Last error found Getting: "Mute". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( log.errLineCount, 3, `getValue: to many lines to stderr ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue of Nothing to stdout and rc=0 should show error message", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", timeout: 500, // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/justExitWithRCof0" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[31mgetValue: Mute function for: MyTelevision returned an empty string ""`, ` getValue incorrect stderr: ${ log.errBuf }` ); // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MyTelevision" getValue. Last error found Getting: "Mute". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( log.errLineCount, 2, `getValue: to many lines to stderr ${ log.errBuf }` ); done( ); }, 1500); }); it( "getValue of Nothing to stdout and rc=1 should show error message", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", timeout: 500, // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/justExitWithRCof0" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[31mgetValue: Mute function for: MyTelevision returned an empty string ""`, ` getValue incorrect stderr: ${ log.errBuf }` ); cmd4Accessory.state_cmd = "./test/echoScripts/justExitWithRCof1"; cmd4Accessory.timeout = 400; // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.errBuf, `getValue Mute function failed for MyTelevision cmd: ./test/echoScripts/justExitWithRCof1 Get 'MyTelevision' 'Mute' Failed.`, ` getValue incorrect stderr: ${ log.errBuf }` ); done( ); }, 700 ); }, 1500 ); }).timeout( 2500 ); it( "getValue of timeout response should fail correctly", function ( done ) { let platformConfig = { accessories: [{ name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", timeout: 400, // Since Mute is optional polling: [{ characteristic: "mute" }], state_cmd: "./test/echoScripts/runToTimeoutRcOf0" }] }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : MyTelevision`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: MyTelevision`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.isFunction( cmd4Accessory.queue.priorityGetValue, "Cmd4Accessory.queue.priorityGetValue is not a function" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).getValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.errBuf, `[31mgetValue Mute function timed out 400ms for MyTelevision cmd: ./test/echoScripts/runToTimeoutRcOf0 Get 'MyTelevision' 'Mute' Failed`, ` getValue incorrect stderr: ${ log.errBuf }` ); done( ); }, 1500 ); }); */ }); ================================================ FILE: test/Cmd4AccessorySetValue.js ================================================ "use strict"; // ***************** TEST LOADING ********************** let { Cmd4Accessory } = require( "../Cmd4Accessory" ); let { Cmd4Platform } = require( "../Cmd4Platform" ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let Characteristic = _api.hap.Characteristic; let Service = _api.hap.Service; let Categories = _api.hap.Categories; let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, Service, Characteristic, Categories ); let getSetValueScript = "./test/echoScripts/testGetSetValues.js"; // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); describe( "Testing Cmd4Accessory", function( ) { it( "Test if Cmd4Accessory exists", function ( ) { expect( Cmd4Accessory ).not.to.be.a( "null", "Cmd4Accessory was null" ); }); it( "Test init Cmd4Accessory", function( done ) { // A config file to play with. let platformConfig = { accessories: [{ name: "Television", type: "Television", displayName: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "Television", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: Television`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); done( ); }); it( "setValue 1 should send 1 to script for ClosedCaption non constant request", function ( done ) { // A config file to play with. let fn = `/tmp/fn1`; let platformConfig = { accessories: [{ name: "Television", type: "Television", displayName: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "Television", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", polling: [{ characteristic: "ClosedCaptions" }], currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", state_cmd_suffix: fn, state_cmd: `node ${ process.cwd( ) }/${ getSetValueScript }` }] }; // Note: We need a characteristic that does not have a verify characteristic // because the getSetValueScript can't seem to handle it. At least not yet. let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); let value = Characteristic.ClosedCaptions.ENABLED; let acc = CMD4_ACC_TYPE_ENUM.ClosedCaptions; cmd4Accessory.log.reset( ); cmd4Accessory.log.setDebugEnabled( false ); // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `Setting Television ClosedCaptions\u001b[39m 1`, ` setValue incorrect stdout: ${ log.logBuf }` ); //ZZZ assert.equal( 1, log.logLineCount, ` setCachedValue logged lines than one: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` setCachedValue unexpected stderr: ${ log.errBuf }` ); assert.equal( 0, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); done( ); }, 1000 ); }); it( `setValue 1, aka ENABLED should send "ENABLED" to script for constant request`, function ( done ) { // A config file to play with. let fn = `/tmp/fn2`; let platformConfig = { accessories: [{ name: "Television", type: "Television", outputConstants: true, displayName: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "Television", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", polling: [{ characteristic: "ClosedCaptions" }], currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", state_cmd: `node ${ process.cwd( ) }/${ getSetValueScript }`, state_cmd_suffix: fn }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); // Note: We need a characteristic that does not have a verify characteristic // because the getSetValueScript can't seem to handle it. At least not yet. let acc = CMD4_ACC_TYPE_ENUM.ClosedCaptions; let value = Characteristic.ClosedCaptions.ENABLED; cmd4Accessory.log.reset( ); cmd4Accessory.log.setDebugEnabled( false ); // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mSetting Television ClosedCaptions\u001b[39m ENABLED`, `incorrect stdout: ${ log.logBuf }` ); //ZZZ assert.equal( 1, log.logLineCount, ` setValue logged lines than one: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` setValue unexpected error output received: ${ log.errBuf }` ); assert.equal( 0, log.errLineCount, ` setValue logged Error lines more than one: ${ log.errBuf }` ); done( ); }, 1000 ); }); it( `Cmd4Accessory should generate warning for publishExternally`, function ( done ) { // A config file to play with. let fn = `/tmp/fn2`; let platformConfig = { accessories: [{ name: "Television", type: "Television", outputConstants: true, displayName: "Television", category: "TELEVISION", active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "Television", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", state_cmd: `node ${ process.cwd( ) }/${ getSetValueScript }`, state_cmd_suffix: fn }] }; // Note: We need a characteristic that does not have a verify characteristic // because the getSetValueScript can't seem to handle it. At least not yet. let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.log.reset( ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); assert.include( log.errBuf, `Televisions should be Platform Accessories with "publishExternally": true`, `Cmd4Accessory incorrect stderr: ${ log.logBuf }` ); assert.equal( 1, log.errLineCount, ` Cmd4Accessory logged Error lines more than one: ${ log.errBuf }` ); done( ); }); it( "setValue true should send 0 to script for Mute request", function ( done ) { // A config file to play with. let fn = `/tmp/fn3`; let platformConfig = { accessories: [{ name: "Television", type: "Television", displayName: "Television", active: true, category: "TELEVISION", publishExternally: true, activeIdentifier: 1234, mute: 1, configuredName: "Television", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", state_cmd_suffix: fn, state_cmd: `node ${ process.cwd( ) }/${ getSetValueScript }` }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: Television`, ` cmd4Accessory incorrect stdout: ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "cmd4Accessory is not an instance of Cmd4Accessory" ); let acc = CMD4_ACC_TYPE_ENUM.Mute; let value = true; cmd4Accessory.log.reset( ); cmd4Accessory.log.setDebugEnabled( false ); cmd4Accessory.log.setOutputEnabled( false ); // Call the setValue bound function, which is prioritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mSetting (Cached) Television Mute\u001b[39m true`, ` setValue incorrect output. received: ${ log.logBuf }` ); assert.equal( 1, log.logLineCount, ` setValue logged lines than one: ${ log.stdout }` ); assert.equal( "", log.errBuf, ` setValue unexpected error output received: ${ log.errBuf }` ); assert.equal( 0, log.errLineCount, ` setValue logged Error lines more than one: ${ log.errBuf }` ); done( ); }, 1000 ); }); it( `setValue of cached "Target*" characteristic, should set "Current*" characteristic`, function ( done ) { // A config file to play with. let platformConfig = { accessories: [ { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, targetTemperature: 20.0, targetHeatingCoolingState: 0, stateChangeResponseTime: 3 }] }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, `Incorrect number of created accessories` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; let acc = CMD4_ACC_TYPE_ENUM.TargetTemperature; let value = 12.3; cmd4Accessory.log.reset( ); // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.include( log.logBuf, `Setting (Cached) Thermostat TargetTemperature\u001b[39m 12.3`, ` setCachedValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Also Setting (Cached) Thermostat CurrentTemperature\u001b[39m 12.3`, ` setCachedValue incorrect stdout: ${ log.logBuf }` ); assert.equal( 2, log.logLineCount, ` setCachedValue logged lines than one: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` setCachedValue unexpected error output received: ${ log.errBuf }` ); assert.equal( 0, log.errLineCount, ` setCachedValue err lines than one: ${ log.errBuf }` ); assert.equal(result, value, " setValue incorrect storedValue. found: " + result ); let relatedCurrentAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[ acc ].relatedCurrentAccTypeEnumIndex; result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( relatedCurrentAccTypeEnumIndex ); assert.equal(result, value, " setValue relatedCurrentAccTypeEnumIndex incorrect stored value " ); done( ); }, 2000 ); }).timeout(3000); it( `In Demo mode, setValue of cached "Target*" characteristic, should set ALSO "Current*" characteristic`, function ( done ) { // A config file to play with. let ThermostatConfig = { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, stateChangeResponseTime: 3 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Accessory = new Cmd4Accessory( log, ThermostatConfig, _api, [ ], null ); assert.include( log.logBuf, `[34mCmd4 is running in Demo Mode`, ` Cmd4Accessory: incorrect stdout: ${ log.logBuf }` ); let acc = CMD4_ACC_TYPE_ENUM.TargetTemperature; let value = 12.3; cmd4Accessory.log.reset( ); // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.include( log.logBuf, `Setting (Cached) Thermostat TargetTemperature\u001b[39m 12.3`, ` setCachedValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Also Setting (Cached) Thermostat CurrentTemperature\u001b[39m 12.3`, ` setCachedValue incorrect stdout: ${ log.logBuf }` ); //assert.equal( 2, log.logLineCount, ` setCachedValue logged lines than one: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` setCachedValue unexpected stderr: ${ log.errBuf }` ); //assert.equal( 0, log.errLineCount, ` setCachedValue err lines than one: ${ log.errBuf }` ); assert.equal(result, value, " setValue incorrect stored value" ); let relatedCurrentAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[ acc ].relatedCurrentAccTypeEnumIndex; result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( relatedCurrentAccTypeEnumIndex ); assert.equal(result, value, " setValue relatedCurrentAccTypeEnum incorrect value" ); done( ); }, 1000 ); }); it( `setValue of cached "Target*" characteristic, should set ALSO "Current*" characteristic`, function ( done ) { // A config file to play with. let ThermostatConfig = { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, stateChangeResponseTime: 1, polling: true, state_cmd: "./Extras/Cmd4Scripts/Examples/AnyDevice" }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Accessory = new Cmd4Accessory( log, ThermostatConfig, _api, [ ], null ); let acc = CMD4_ACC_TYPE_ENUM.TargetTemperature; let value = 12.3; cmd4Accessory.log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.include( log.logBuf, `Setting Thermostat TargetTemperature\u001b[39m 12.3`, ` setCachedValue incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: CurrentTemperature function for: Thermostat returned: 12.3`, ` setValue incorrect stdout: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` setValue unexpected stderr: ${ log.errBuf }` ); assert.equal( 0, log.errLineCount, ` setValue err lines than one: ${ log.errBuf }` ); assert.equal(result, value, " setValue incorrect stored value" ); let relatedCurrentAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[ acc ].relatedCurrentAccTypeEnumIndex; result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( relatedCurrentAccTypeEnumIndex ); assert.equal(result, value, " setValue relatedCurrentAccTypeEnum incorrect value" ); done( ); }, 2500 ); }).timeout( 3000 ); it( `Missing required characteristic should generate a warning and add the characteristic`, function ( done ) { // A config file to play with. let ThermostatConfig = { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, stateChangeResponseTime: 3 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Accessory = new Cmd4Accessory( log, ThermostatConfig, _api, [ ], null ); let acc = CMD4_ACC_TYPE_ENUM.TargetTemperature; assert.include( log.logBuf, `[34mCmd4 is running in Demo Mode`, ` Cmd4Accessory: incorrect stdout: ${ log.logBuf }` ); assert.equal( 1, log.logLineCount, ` setCachedValue logged lines than one: ${ log.logBuf }` ); assert.include( log.errBuf, `m**** Adding required characteristic TargetTemperature for Thermostat`, ` setCachedValue incorrect stdout:${ log.errBuf }` ); assert.include( log.errBuf, `Not defining a required characteristic can be problematic`, ` setCachedValue incorrect stdout: ${ log.errBuf }` ); assert.equal( 2, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); let defaultValue = CMD4_DEVICE_TYPE_ENUM.properties[ cmd4Accessory.typeIndex ].requiredCharacteristics.find( key => key.type === acc ).defaultValue; let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.equal(result, defaultValue, ` setValue incorrect stored value` ); done( ); }); it( `In Demo mode, Missing Optional characteristic should generate a warning and add the characteristic`, function ( done ) { // A config file to play with. let ThermostatConfig = { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, // targetHeatingCoolingState: 0, stateChangeResponseTime: 3 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); new Cmd4Accessory( log, ThermostatConfig, _api, [ ], null ); assert.include( log.logBuf, `[34mCmd4 is running in Demo Mode`, ` Cmd4Accessory: incorrect stdout: ${ log.logBuf }` ); assert.equal( 1, log.logLineCount, ` setCachedValue logged lines than one: ${ log.logBuf }` ); assert.include( log.errBuf, `**** Adding required characteristic TargetHeatingCoolingState for Thermostat`, ` setCachedValue incorrect stderr: ${ log.errBuf }` ); assert.include( log.errBuf, `Not defining a required characteristic can be problematic`, ` setCachedValue incorrect stderr: ${ log.errBuf }` ); assert.equal( 2, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); done( ); }); it( `Missing Optional characteristic should generate a warning and add the characteristic`, function ( done ) { // A config file to play with. let ThermostatConfig = { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, // targetHeatingCoolingState: 0, stateChangeResponseTime: 3, polling: true, state_cmd: "./test/echoScripts/echo_1" }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); new Cmd4Accessory( log, ThermostatConfig, _api, [ ], null ); assert.equal( log.logBuf, "", ` Cmd4Accessory: unexpected stdout: ${ log.logBuf }` ); assert.equal( 0, log.logLineCount, ` Cmd4Accessory: incorrect number of lines to stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `**** Adding required characteristic TargetHeatingCoolingState for Thermostat`, ` setCachedValue incorrect stderr: ${ log.errBuf }` ); assert.include( log.errBuf, `Not defining a required characteristic can be problematic`, ` setCachedValue incorrect stderr: ${ log.errBuf }` ); assert.equal( 2, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); done( ); }); it( `In Demo mode, setValue of cached characteristic , should not set Current*" characteristic on TemperatureSensor`, function ( done ) { // A config file to play with. let TempSensorConfig = { type: "TemperatureSensor", name: "TemperatureSensor", displayName: "TemperatureSensor", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Accessory = new Cmd4Accessory( log, TempSensorConfig, _api, [ ], null ); let acc = CMD4_ACC_TYPE_ENUM.CurrentTemperature; let value = 12.3; cmd4Accessory.log.reset( ); // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); // You cannot set a read only value for a Sensor assert.equal( log.logBuf, ``, ` setCachedValue should not occur, incorrect stdout: ${ log.logBuf }` ); assert.equal( 0, log.logLineCount, ` setCachedValue logged lines than one: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` setCachedValue logged an error: ${ log.errBuf }` ); assert.equal( 0, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); // The value should not be changed and be what is in the config.json assert.equal(result, 20.0, " setValue incorrect value" ); let relatedTargetAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[ acc ].relatedTargetAccTypeEnumIndex; result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( relatedTargetAccTypeEnumIndex ); assert.isNull(result, ` getValue TargetAccTypeEnumIndex expected null to be stored.` ); done( ); }, 1000 ); }); it( `setValue of cached characteristic , should not set Current*" characteristic on TemperatureSensor`, function ( done ) { // A config file to play with. let TempSensorConfig = { type: "TemperatureSensor", name: "TemperatureSensor", displayName: "TemperatureSensor", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, polling: true, state_cmd: "./Extras/Cmd4Scripts/Examples/AnyDevice" }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Accessory = new Cmd4Accessory( log, TempSensorConfig, _api, [ ], null ); let acc = CMD4_ACC_TYPE_ENUM.CurrentTemperature; let value = 12.3; cmd4Accessory.log.reset( ); // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ acc ] .characteristic ).setValue( value, function dummyCallback( ) { } ); setTimeout( ( ) => { let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); // You cannot set a read only value for a Sensor assert.equal( log.logBuf, ``, ` setCachedValue should not occur, incorrect stdout: ${ log.logBuf }` ); assert.equal( 0, log.logLineCount, ` setCachedValue logged lines than one: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` setCachedValue logged an error: ${ log.errBuf }` ); assert.equal( 0, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); // The value should not be changed and be what is in the config.json assert.equal(result, 20.0, " setValue incorrect value" ); let relatedTargetAccTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties[ acc ].relatedTargetAccTypeEnumIndex; result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( relatedTargetAccTypeEnumIndex ); assert.isNull(result, ` getValue TargetAccTypeEnumIndex expected null to be stored.` ); done( ); }, 1000 ); }); it( "setValue of timeout response should fail correctly", function ( done ) { // A config file to play with. let TVConfig = { name: "MyTelevision", type: "Television", category: "TELEVISION", publishExternally: true, active: "ACTIVE", activeIdentifier: 1234, mute: true, configuredName: "MyTelevision", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", brightness: 8, closedCaptions: "DISABLED", currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT", timeout: 401, polling: [{ characteristic: "Mute" }], state_cmd: "./test/echoScripts/runToTimeoutRcOf0" }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Accessory = new Cmd4Accessory( log, TVConfig, _api, [ ], null ); cmd4Accessory.timeout = 400; // Call the setValue bound function, which is priorritySetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Mute ] .characteristic ).setValue( "Mute", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[34mSetting MyTelevision Mute\u001b[39m 0`, ` setValue output something to stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[31m\u001b[31msetValue Mute function failed for MyTelevision cmd: ./test/echoScripts/runToTimeoutRcOf0 Set 'MyTelevision' 'Mute' '0' Failed`, ` setValue incorrect stderr: ${ log.errBuf }` ); done( ); }, 1000 ); }); }); ================================================ FILE: test/Cmd4Mode.js ================================================ "use strict"; // ***************** TEST LOADING ********************** let { Cmd4Platform } = require( "../Cmd4Platform" ); let { Cmd4Accessory } = require( "../Cmd4Accessory" ); // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); describe( "Testing Demo Mode", function( ) { beforeEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; }); it( "Test if Cmd4Accessory exists", function ( ) { expect( Cmd4Accessory ).not.to.be.a( "null", "Cmd4Accessory was null" ); }); it( "V2 Crippled Test that getValue (Cached) occurs in Demo mode", function( done ) { let platformConfig = { accessories: [{ name: "MySwitch", type: "Switch", on: false, }] }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[35mConfiguring platformAccessory: \u001b[39mMySwitch`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); assert.include( log.logBuf, `[33mAdding getCachedValue for MySwitch characteristic: On`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "Cmd4Accessory is not an instance of Cmd4Accessory" ); //log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); /* Characteristic.getValue() is deprecated in V2 and Characteristic.value does not call the * get functions. Worked last in homebridge-1.8 // Call the getValue bound function, which is priorityGetValue cmd4Accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ] .characteristic ).getValue( "On", function dummyCallback( ) { } ); setTimeout( ( ) => { assert.include( log.logBuf, `[90mgetCachedValue On for: MySwitch returned (CACHED) value: false`, ` getValue incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1000 ); */ // Added for homebridge v2 .getValue lines commented out above. done(); }).timeout( 2000 ); }); ================================================ FILE: test/Cmd4Platform.js ================================================ #!node // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); let constants = require( "../cmd4Constants" ); let Cmd4Accessory = require( "../Cmd4Accessory" ).Cmd4Accessory; let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; let Cmd4PriorityPollingQueue = require( "../Cmd4PriorityPollingQueue" ).Cmd4PriorityPollingQueue; var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_CHAR_TYPE_ENUMS = CHAR_DATA.init( _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // Taken from https://stackoverflow.com/questions/11731072/dividing-an-array-by-filter-function //function partition(array, predicate) //{ // return array.reduce( ( acc, item ) => ( acc[+!predicate( item )].push( item ), acc ), [ [], [] ] ); //} // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); describe( "Quick Test of CMD4_CHAR_TYPE_ENUM", ( ) => { it( "CMD4_CHAR_TYPE_ENUMS should be defined ( required correctly )", ( ) => { assert.isNotNull( CMD4_CHAR_TYPE_ENUMS, "CMD4_CHAR_TYPE_ENUMS is null" ); }); }); describe('Testing Cmd4Platform Init', ( ) => { before( ( ) => { sinon.stub( process, `exit` ); }); after( ( ) => { process.exit.restore( ); }); beforeEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach(function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; }); it( "Test if Cmd4Platform exists", function ( ) { expect( Cmd4Platform ).not.to.be.a( "null", "Cmd4Platform was null" ); }); it( "Test creation of Cmd4Platform", function( done ) { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); assert.equal( "", log.logBuf, ` Cmd4Platform unexpected stdout received: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` Cmd4Platform unexpected stderr received: ${ log.errBuf }` ); done( ); }); it('Test if Cmd4Platform creates a platform accessory', ( done ) => { let platformConfig = { accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); assert.include( log.logBuf, `Adding new platformAccessory: MyDoor`, ` Cmd4Platform Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `35mConfiguring platformAccessory: \u001b[39mMyDoor`, ` Cmd4Platform Incorrect stdout: ${ log.logBuf}` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); assert.equal( cmd4Accessory.CMD4, constants.PLATFORM, ` Created accessory was not a PLATFORM accessory` ); done( ); }); it('Test if OutputConstants are used from the accessory', ( done ) => { let platformConfig = { outputConstants: true, accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); assert.equal( cmd4Platform.outputConstants, true, ` Created Platform has incorrect OutputConstants` ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; // it's a Hierarhy variable assert.equal( cmd4Accessory.hV.outputConstants, true, ` Created Accessory has incorrect OutputConstants` ); done( ); }); it('Test if outputConstants are used from the accessory', ( done ) => { let platformConfig = { outputConstants: true, accessories: [ { outputConstants: false, name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); // Heirarchy variable assert.equal( cmd4Platform.hV.outputConstants, true, ` Created Platform has incorrect OutputConstants` ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; // it's a Hierarhy variable assert.equal( cmd4Accessory.hV.outputConstants, false, ` Created Accessory has incorrect outputConstants` ); done( ); }); it('Test if interval, timeout, outputConstants, stateChangeResponseTime are used from the platform', ( done ) => { let platformConfig = { timeout: 12345, interval: 12, stateChangeResponseTime: 18, outputConstants: true, accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; assert.equal( Object.keys( cmd4Accessory.listOfPollingCharacteristics ).length, 3, `Incorret number of polling characteristics` ); // it's a Hierarhy variable assert.equal( cmd4Accessory.hV.timeout, 12345, `Timeout was not passed down to accessory` ); assert.equal( cmd4Accessory.hV.interval, 12000, `Interval was not passed down to accessory` ); assert.equal( cmd4Accessory.hV.stateChangeResponseTime, 18000, `stateChangeResponseTime was not passed down to accessory` ); assert.equal( cmd4Accessory.hV.outputConstants, true, `outputConstants was not passed down to accessory` ); Object.keys( cmd4Accessory.listOfPollingCharacteristics ).forEach( ( key ) => { let entry = cmd4Accessory.listOfPollingCharacteristics[ key ]; assert.equal( entry.timeout, 12345, `Timeout was not passed down to polling entry` ); assert.equal( entry.interval, 12000, `Interval was not passed down to polling entry` ); assert.equal( entry.stateChangeResponseTime, 18000, `stateChangeResponseTime was not passed down to polling entry` ); }); done( ); }); it('Test outputConstants, stateChangeResponseTime are used from the accessory definition', ( done ) => { let platformConfig = { timeout: 12345, interval: 12, accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", outputConstants: true, currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); // it's a Hierarhy variable assert.equal( cmd4Platform.hV.outputConstants, false, ` Created Platform has incorrect OutputConstants` ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; assert.equal( Object.keys( cmd4Accessory.listOfPollingCharacteristics ).length, 3, `Incorret number of polling characteristics` ); // it's a Hierarhy variable assert.equal( cmd4Accessory.hV.timeout, 12345, `Timeout was not passed down to accessory` ); assert.equal( cmd4Accessory.hV.interval, 12000, `Interval was not passed down to accessory` ); assert.equal( cmd4Accessory.hV.stateChangeResponseTime, constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, `stateChangeResponseTime was not passed from CMD4_DEVICE_TYPE_ENUM` ); assert.equal( cmd4Accessory.hV.outputConstants, true, ` Created Accessory has incorrect OutputConstants` ); Object.keys( cmd4Accessory.listOfPollingCharacteristics ).forEach( ( key ) => { let entry = cmd4Accessory.listOfPollingCharacteristics[ key ]; assert.equal( entry.timeout, 12345, `Timeout was not passed down to polling entry` ); assert.equal( entry.interval, 12000, `Interval was not passed down to polling entry` ); assert.equal( entry.stateChangeResponseTime, constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, `stateChangeResponseTime was not passed from CMD4_DEVICE_TYPE_ENUM` ); }); done( ); }); it('Test stateChangeResponseTime default is used for PriorityQueuedPolling', ( done ) => { let platformConfig = { timeout: 12345, interval: 12, accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; assert.equal( Object.keys( cmd4Accessory.listOfPollingCharacteristics ).length, 3, `Incorret number of polling characteristics` ); // it's a Hierarhy variable assert.equal( cmd4Accessory.hV.timeout, 12345, `Timeout was not passed down to accessory` ); assert.equal( cmd4Accessory.hV.interval, 12000, `Interval was not passed down to accessory` ); assert.equal( cmd4Accessory.hV.stateChangeResponseTime, constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, `stateChangeResponseTime was not passed from CMD4_DEVICE_TYPE_ENUM` ); Object.keys( cmd4Accessory.listOfPollingCharacteristics ).forEach( ( key ) => { let entry = cmd4Accessory.listOfPollingCharacteristics[ key ]; assert.equal( entry.timeout, 12345, `Timeout was not passed down to polling entry` ); assert.equal( entry.interval, 12000, `Interval was not passed down to polling entry` ); assert.equal( entry.stateChangeResponseTime, constants.MEDIUM_STATE_CHANGE_RESPONSE_TIME, `stateChangeResponseTime was not passed from CMD4_DEVICE_TYPE_ENUM` ); }); done( ); }); it('Test Cmd4Platform divies queues from staggered polling', ( done ) => { let platformConfig = { timeout: 12345, interval: 12, accessories: [ { name: "MySwitch", displayName: "MySwitch", type: "Switch", on: 0, polling: [ { characteristic: "on" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 2, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory1 = cmd4Platform.createdCmd4Accessories[0]; let cmd4Accessory2 = cmd4Platform.createdCmd4Accessories[1]; assert.equal( Object.keys( cmd4Accessory1.listOfPollingCharacteristics ).length, 1, `Incorret number of polling characteristics for accessory 1` ); assert.equal( Object.keys( cmd4Accessory2.listOfPollingCharacteristics ).length, 3, `Incorret number of polling characteristics for accessory 2` ); let numberOfQueues = Object.keys( settings.listOfCreatedPriorityQueues ).length; assert.equal( numberOfQueues, 2, `Incorrect number of polling queues` ); let queue = settings.listOfCreatedPriorityQueues[ "Q:MyDoor" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PriorityPollingQueue" ); // Low priority queues are continious, make sure it is put back assert.equal( queue.lowPriorityQueue.length, 3, `low priority queue should init to size 3` ); done( ); }); // Until we remove the successful add, it screw up other tests it.skip('Test definition can be created successfully', ( done ) => { let platformConfig = { definitions: [{ type: "PointX", description: "An X Coordinate", props: { format: "uint32", minValue: 0, minStep: 1, perms: [ "pr", "pw", "ev" ] }, validValues: { } }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); assert.include( log.logBuf, `Processing definition index: 0`, ` Cmd4Platform Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Created definition type: "PointX".`, ` Cmd4Platform Incorrect stdout: ${ log.logBuf }` ); done( ); }); // Until we remove the successful add, it screw up other tests it.skip('Test definition with no validValues can be created successfully', ( done ) => { let platformConfig = { definitions: [{ type: "PointX", description: "An X Coordinate", props: { format: "uint32", minValue: 0, minStep: 1, perms: [ "pr", "pw", "ev" ] } }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], State_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 1, ` Cmd4Platform did not create the cmd4Accessory` ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); assert.include( log.logBuf, `Processing definition index: 0`, ` Cmd4Platform Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Created definition type: "PointX".`, ` Cmd4Platform Incorrect stdout: ${ log.logBuf }` ); done( ); }); it('Test definition type must be string or throws error', ( done ) => { let platformConfig = { definitions: [{ type: true, description: "An X Coordinate", props: { format: "uint32", minValue: 0, minStep: 1, perms: [ "pr", "pw", "ev" ] }, validValues: { } }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect( ( ) => new Cmd4Platform( log, platformConfig, _api )).to.throw(/definition.type at index: 0 is not a String./); done( ); }); it('Test definition description must be string or throws error', ( done ) => { let platformConfig = { definitions: [{ type: "PointX", props: { format: "uint32", minValue: 0, minStep: 1, perms: [ "pr", "pw", "ev" ] }, validValues: { } }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect( ( ) => new Cmd4Platform( log, platformConfig, _api )).to.throw(/definition.description at index: 0 is not a String./); done( ); }); it('Test definition props must be an Object or throws error', ( done ) => { let platformConfig = { definitions: [{ type: "PointX", description: "A point on X axis", props: [], validValues: { } }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect( ( ) => new Cmd4Platform( log, platformConfig, _api )).to.throw(/definition.props at index: 0 is not an Object./); done( ); }); it('Test definition props.format must be string or throws error', ( done ) => { let platformConfig = { definitions: [{ type: "PointX", description: "A point on X axis", props: { format: 0, minValue: 0, minStep: 1, perms: [ "pr", "pw", "ev" ] }, validValues: { } }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect( ( ) => new Cmd4Platform( log, platformConfig, _api )).to.throw(/definition.props.format at index: 0 is not a String./); done( ); }); it('Test definition props.format must be valid or throws error', ( done ) => { let platformConfig = { definitions: [{ type: "PointX", description: "A point on X axis", props: { format: "ZERO", minValue: 0, minStep: 1, perms: [ "pr", "pw", "ev" ] }, validValues: { } }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); expect( ( ) => new Cmd4Platform( log, platformConfig, _api )).to.throw(/definition.props.format at index: 0 is not a valid format./); done( ); }); it('Test definition validValues must be an Object or throws error', ( done ) => { let platformConfig = { definitions: [{ type: "PointX", description: "A point on X axis", props: { format: "uint32", minValue: 0, minStep: 1, perms: [ "pr", "pw", "ev" ] }, validValues: 1 }], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); expect( ( ) => new Cmd4Platform( log, platformConfig, _api )).to.throw(/definition.validValues at index: 0 is not an Object./); done( ); }); it('CameraControl throws error for deprecated device', ( ) => { let platformConfig = { accessories: [ { statusMsg: true, type: "CameraControl", displayName: "CameraControl", name: "CameraControl", on: "1", currentHorizontalTiltAngle: 12, polling: [ { characteristic: "currentHorizontalTiltAngle" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect ( ( ) => cmd4Platform.discoverDevices( ) ).to.throw(/Error: device type: "CameraControl" is now deprecated in Homebridge/); }); }); ================================================ FILE: test/Cmd4PlatformRestartTests.js ================================================ "use strict"; const settings = require( "../cmd4Settings" ); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; // For caching/uncaching accessories to disk, like homebridge does. const node_persist_1 = __importDefault(require("node-persist")); const path = require( "path" ); const TEST_BASE_DIR = path.join(__dirname, "tmp"); const rmdir = require('rimraf'); function rand(prefix) { return (prefix ? prefix + '-' : '') + (+ new Date()) + '-' + Math.floor(Math.random() * 1000); } function randDir () { //return path.join(TEST_BASE_DIR, '/' + "static" ); return path.join(TEST_BASE_DIR, '/' + rand()); } function loadCachedPlatformAccessoriesFromDisk( accessoryStorage, cachedAccessoriesFile = "accessories" ) { let cachedAccessories; cachedAccessories = accessoryStorage.getItemSync( cachedAccessoriesFile ); if ( cachedAccessories ) { return cachedAccessories.map(serialized => { return platformAccessory_1.PlatformAccessory.deserialize(serialized); }); } else { console.log( `Woops, missing ${ cachedAccessoriesFile }` ); } } function restoreCachedPlatformAccessories( cmd4Platform, cachedPlatformAccessories ) { if ( cachedPlatformAccessories._associatedPlatform == settings.PLATFORM_NAME ) { //console.log("stuffing:%s", cachedPlatformAccessories ); cmd4Platform.configureAccessory( cachedPlatformAccessories ); } } function saveCachedPlatformAccessoriesOnDisk( cachedPlatformAccessories, accessoryStorage, cachedAccessoriesFile = "accessories" ) { if ( cachedPlatformAccessories && cachedPlatformAccessories.length > 0 ) { const serializedAccessories = cachedPlatformAccessories.map(Cmd4Accessory => platformAccessory_1.PlatformAccessory.serialize(Cmd4Accessory)); accessoryStorage.setItemSync( cachedAccessoriesFile, serializedAccessories ); } else { console.log( `Woops, cachedPlatformAccessories ${ cachedPlatformAccessories } is empty/null` ); } } var _api = new HomebridgeAPI(); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; // A config file to play with. let TVConfig = { platform: "Cmd4", outputConstants: false, cmd4_Mode: "Demo", accessories: [ { type: "Television", category: "TELEVISION", publishExternally: false, name: "Example TV", active: "ACTIVE", activeIdentifier: 1, configuredName: "Example TV", manufacturer: "Example TV Mfg.", model: "Example TV Model", serialNumber: "Example TV Sn.", sleepDiscoveryMode: "ALWAYS_DISCOVERABLE", accessories: [ { type: "TelevisionSpeaker", displayName: "My TvSpeaker", name: "My TVSpeaker", active: "ACTIVE", mute: "FALSE", volumeSelector: 1, volume: 10, volumeControlType: "ABSOLUTE" } ], linkedTypes: [ { type: "InputSource", displayName: "HDMI1", configuredName: "HDMI 1", currentVisibilityState: "SHOWN", inputSourceType: "HDMI", isConfigured: "CONFIGURED", identifier: 1, targetVisibilityState: "SHOWN", manufacturer: "HDMI 1 Mfg.", model: "HDMI 1 Model", serialNumber: "HDMI 1 Sn.", name: "HDMI 1" }, { type: "InputSource", displayName: "HDMI 2", configuredName: "HDMI 2", currentVisibilityState: "SHOWN", inputSourceType: "HDMI", isConfigured: "CONFIGURED", identifier: 2, targetVisibilityState: "SHOWN", manufacturer: "HDMI 2 Mfg.", model: "HDMI 2 Model", serialNumber: "HDMI 2 Sn.", name: "HDMI 2" }, { type: "InputSource", displayName: "Netflix", configuredName: "Netflix", currentVisibilityState: "SHOWN", inputSourceType: "HDMI", isConfigured: "CONFIGURED", identifier: 3, targetVisibilityState: "SHOWN", manufacturer: "Netflix Mfg.", model: "Netflix Model", serialNumber: "Netflix Sn.", name: "Netflix" } ], displayOrder: 0, currentMediaState: "STOP", targetMediaState: "STOP", pictureMode: "STANDARD", remoteKey: "SELECT" } ] }; // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); describe( "Testing Cmd4Platform Setup", function( ) { it( "Test if Cmd4Platform exists", function ( ) { expect( Cmd4Platform ).not.to.be.a( "null", "Cmd4Platform was null" ); }); it( "Test init Cmd4Platform", function( ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, TVConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); //expect( cmd4Platform.createdCmd4Platforms.length ).to.equal( 1, "cmd4Platform.createdCmd4Platforms.length is not 1. Found:" + cmd4Platform.createdCmd4Platforms.length ); }); it( "Test node-persist", function( ) { let rc; let options = { dir: randDir(), // logging: true }; //after( function( done ) // { // rmdir( options.dir, done); //}); const accessoryStorage = node_persist_1.default.create(); accessoryStorage.initSync( options ); // Setup Accessory Cache Storage assert.ok( fs.existsSync( options.dir ) ); accessoryStorage.setItem( "name", "yourName" ); rc = accessoryStorage.getItem('name'); assert.equal(rc, 'yourName', `write/read didn't work`); accessoryStorage.clear(); }); }); describe( "Testing Cmd4Platform", function( ) { const accessoryStorage = node_persist_1.default.create(); let options = { dir: randDir(), // logging: true }; beforeEach(async function( ) { await accessoryStorage.init( options ); }); after( function( done ) { rmdir( options.dir, done ); }); it( "Test trigger of Cmd4Platform.didFinishLoading", function( ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform=new Cmd4Platform( log, TVConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); // Instead of emitting didFinishLaunching which would cause other // instances to also do the didFinishLaunching and start their polling // as well, cmd4Platform.discoverDevices( ); assert.equal(cmd4Platform.createdCmd4Platforms.length, 1, `Incorrect number of Cmd4Accessories created. result: ${ cmd4Platform.createdCmd4Platforms.length }` ); saveCachedPlatformAccessoriesOnDisk( cmd4Platform.createdCmd4Platforms, accessoryStorage ) loadCachedPlatformAccessoriesFromDisk( accessoryStorage ); }); it( "Test reload of saved Platforms with value change to disk", function( ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // We need our own instance as emitting "didFinishLaunching" triggers other testcases let cmd4Platform = new Cmd4Platform( log, TVConfig, _api ); // Instead of emitting didFinishLaunching which would cause other // instances to also do the didFinishLaunching and start their polling // as well, cmd4Platform.discoverDevices( ); assert.equal(cmd4Platform.createdCmd4Platforms.length, 1, `Incorrect number of Cmd4Platforms created. result: ${ cmd4Platform.createdCmd4Platforms.length }` ); assert.equal(cmd4Platform.createdCmd4Accessories.length, 5, `Incorrect number of Cmd4Accessories created. result: ${ cmd4Platform.createdCmd4Accessories.length }` ); // This would have been already tested in Cmd4AccessorySetValue.js let cmd4Accessory = cmd4Platform.createdCmd4Accessories[4]; let acc = CMD4_ACC_TYPE_ENUM.ConfiguredName; let foundConfiguredName = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.equal( foundConfiguredName, TVConfig.accessories[0].configuredName, `Incorrect configuredName: ${ foundConfiguredName }` ); let newValue = "NEW_TV"; log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); cmd4Accessory.setCachedValue( acc, "ConfiguredName", newValue, function( rc ) { assert.equal( rc, null, `setCachedValue expected: zero received: ${ rc }` ); assert.include( log.logBuf, `[34mSetting (Cached) Example TV ConfiguredName\u001b[39m NEW_TV`, `setCachedValue Incorrect stdout: ${ log.logBuf }` ); let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.equal( result, newValue, " setCachedValue Incorrect result" ); // Clear the log buffer for next time. log.reset(); saveCachedPlatformAccessoriesOnDisk( cmd4Platform.createdCmd4Platforms, accessoryStorage ) // Simulate a restart of homebridge with a new Cmd4Platform instance and the stored date reloaded. let cachedPlatformAccessories = loadCachedPlatformAccessoriesFromDisk( accessoryStorage ); log.debug(" ***** RUNNING NEXT PLATFORM ***** " ); let cmd4Platform2 = new Cmd4Platform( log, TVConfig, _api ); cachedPlatformAccessories.forEach( ( entry ) => { restoreCachedPlatformAccessories( cmd4Platform2, entry ); }); // Instead of emitting didFinishLaunching which would cause other // instances to also do the didFinishLaunching and start their polling // as well, cmd4Platform2.discoverDevices( ); cmd4Accessory = cmd4Platform2.createdCmd4Accessories[4]; let newFoundConfiguredName = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.equal( newFoundConfiguredName, newValue, `Incorrect configuredName: ${ newFoundConfiguredName }` ); }); //restoreCachedPlatformAccessories( cmd4Platform, cachedPlatformAccessories ); //cmd4Platform.configureAccessory( cachedPlatformAccessories ); //expect( cmd4Platform.createdCmd4Accessories.length ).to.equal( 1, "cmd4Platform.createdCmd4Accessories.length is not 1. Found:" + cmd4Platform.createdCmd4Accessories.length ); }); it( "Test reload of saved Platforms with value change to disk", function( ) { let SwitchConfig = { platform: "Cmd4", accessories : [ { type: "Switch", name: "Example Switch", configuredName: "Example Switch", on: 0 } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // We need our own instance as emitting "didFinishLaunching" triggers other testcases let cmd4Platform = new Cmd4Platform( log, SwitchConfig, _api ); // Instead of emitting didFinishLaunching which would cause other // instances to also do the didFinishLaunching and start their polling // as well, cmd4Platform.discoverDevices( ); assert.equal(cmd4Platform.createdCmd4Platforms.length, 1, `Incorrect number of Cmd4Platforms created. result: ${ cmd4Platform.createdCmd4Platforms.length }` ); assert.equal(cmd4Platform.createdCmd4Accessories.length, 1, `Incorrect number of Cmd4Accessories created. result: ${ cmd4Platform.createdCmd4Accessories.length }` ); // This would have been already tested in Cmd4AccessorySetValue.js let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; let acc = CMD4_ACC_TYPE_ENUM.ConfiguredName; let foundConfiguredName = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.equal( foundConfiguredName, SwitchConfig.accessories[0].configuredName, `Incorrect configuredName: ${ foundConfiguredName }` ); let newValue = "NEW_Switch"; log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); cmd4Accessory.setCachedValue( acc, "ConfiguredName", newValue, function( rc ) { cmd4Accessory.configuredName=newValue; assert.equal( rc, null, `setCachedValue expected: zero received: ${ rc }` ); assert.include( log.logBuf, `[34mSetting (Cached) Example Switch ConfiguredName\u001b[39m NEW_Switch`, `setCachedValue Incorrect stdout: ${ log.logBuf }` ); let result = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.equal( result, newValue, " setCachedValue Incorrect result" ); // Clear the log buffer for next time. log.reset(); saveCachedPlatformAccessoriesOnDisk( cmd4Platform.createdCmd4Platforms, accessoryStorage ) // Simulate a restart of homebridge with a new Cmd4Platform instance and the stored date reloaded. let cachedPlatformAccessories = loadCachedPlatformAccessoriesFromDisk( accessoryStorage ); log.debug(" ***** RUNNING NEXT PLATFORM ***** " ); let cmd4Platform2 = new Cmd4Platform( log, SwitchConfig, _api ); cachedPlatformAccessories.forEach( ( entry ) => { restoreCachedPlatformAccessories( cmd4Platform2, entry ); }); // Instead of emitting didFinishLaunching which would cause other // instances to also do the didFinishLaunching and start their polling // as well, cmd4Platform2.discoverDevices( ); cmd4Accessory = cmd4Platform2.createdCmd4Accessories[0]; let newFoundConfiguredName = cmd4Accessory.cmd4Storage.getStoredValueForIndex( acc ); assert.equal( newFoundConfiguredName, newValue, `Incorrect configuredName: ${ newFoundConfiguredName }` ); }); //restoreCachedPlatformAccessories( cmd4Platform, cachedPlatformAccessories ); //cmd4Platform.configureAccessory( cachedPlatformAccessories ); //expect( cmd4Platform.createdCmd4Accessories.length ).to.equal( 1, "cmd4Platform.createdCmd4Accessories.length is not 1. Found:" + cmd4Platform.createdCmd4Accessories.length ); }); it( "Test fix of bug#130, Linked types not accessable after restart", function( ) { // Note: I tested this by changing Cmd4Platform.js and putting back the old code and it failed. so this test is valid. the code being: // ** WRONG ** // linkedAccessory.service = linkedAccessory.platform.getService( devProperties.service, linkedAccessory.name, linkedAccessory.subType ); // ** CORRECT ** // linkedAccessory.service = linkedAccessory.platform.getService( linkedAccessory.name, linkedAccessory.subType ); let LightConfig = { platform: "Cmd4", accessories: [{ type: "Lightbulb", displayName: "Light1", on: 0, stateChangeResponseTime: 1, brightness: 100, hue: 200, saturation: 100, state_cmd: "node .homebridge/Cmd4Scripts/State.js", // polling: [ { characteristic: "on" }, // { characteristic: "brightness" } // ], linkedTypes: [{ type: "Lightbulb", displayName: "Light2", on: 0, brightness: 0, stateChangeResponseTime: 1, state_cmd: "node .homebridge/Cmd4Scripts/State.js", // polling: [ { characteristic: "on" }, // { characteristic: "brightness" } // ] }] }] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // We need our own instance as emitting "didFinishLaunching" triggers other testcases let cmd4Platform = new Cmd4Platform( log, LightConfig, _api ); // Instead of emitting didFinishLaunching which would cause other // instances to also do the didFinishLaunching and start their polling // as well, cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `Creating linked accessories for: Light1`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding getCachedValue for Light2 characteristic: Brightness`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); saveCachedPlatformAccessoriesOnDisk( cmd4Platform.createdCmd4Platforms, accessoryStorage ) // Simulate a restart of homebridge with a new Cmd4Platform instance and the stored date reloaded. let cachedPlatformAccessories = loadCachedPlatformAccessoriesFromDisk( accessoryStorage ); log.debug(" ***** RUNNING NEXT PLATFORM ***** " ); let cmd4Platform2 = new Cmd4Platform( log, LightConfig, _api ); cachedPlatformAccessories.forEach( ( entry ) => { restoreCachedPlatformAccessories( cmd4Platform2, entry ); }); // Instead of emitting didFinishLaunching which would cause other // instances to also do the didFinishLaunching and start their polling // as well, cmd4Platform2.discoverDevices( ); assert.include( log.logBuf, `Creating linked accessories for: Light1`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding getCachedValue for Light2 characteristic: Brightness`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); }); // Next testcase // saveCachedPlatformAccessoriesOnDisk( cachedPlatformAccessories, accessoryStorage, cachedAccessoryPath, cachedAccessoriesFile = "cachedAccessories" ) }); ================================================ FILE: test/Cmd4PriorityPollingQueue.js ================================================ #!node // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); let constants = require( "../cmd4Constants" ); let Cmd4Accessory = require( "../Cmd4Accessory" ).Cmd4Accessory; let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; let Cmd4PriorityPollingQueue = require( "../Cmd4PriorityPollingQueue" ).Cmd4PriorityPollingQueue; // Duplicated from Cmd4PriorityPollingQueue.js let HIGH_PRIORITY_SET = 0; let HIGH_PRIORITY_GET = 1; //let LOW_PRIORITY_GET = 2; var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); function dummyCallback( ) { } // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); describe('WoRm - Testing Cmd4PriorityPollingQueue polling', ( ) => { // So we can cancel any timers let cmd4PriorityPollingQueue; before( ( ) => { //sinon.stub( process, `exit` ); cleanStatesDir(); }); after( ( ) => { //process.exit.restore( ); }); beforeEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; // MaxListenersExceededWarning: Possible EventEmitter memory leak detected _api.removeAllListeners(); }); it( "WoRm - Test if Cmd4PriorityPollingQueue exists", function ( ) { expect( Cmd4PriorityPollingQueue ).not.to.be.a( "null", "Cmd4PriorityPollingQueue was null" ); }); it( "WoRm - Test echoRetryErrors will echo approprietly", function( ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let queueName = "Queue A"; cmd4PriorityPollingQueue = new Cmd4PriorityPollingQueue( log, queueName ); expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "cmd4PollingQueues is not an instance of Cmd4PollingQueues" ); assert.isFunction( cmd4PriorityPollingQueue.echoRetryErrors, ` Cmd4PriorityPollingQueue.echoRetryErrors is not a function` ); assert.equal( cmd4PriorityPollingQueue.queueRetryCount, 0, ` Cmd4PriorityPollingQueue.queueRetryCount should be 0` ); // When count is zero then printing error should be true let count = 0; cmd4PriorityPollingQueue.queueRetryCount = 0; // Default assert.isTrue( cmd4PriorityPollingQueue.echoRetryErrors( count ), ` Cmd4PriorityPollingQueue.echoRetryErrors( ${ count } ) for worm queue should not be true` ); assert.isTrue( cmd4PriorityPollingQueue.echoRetryErrors( count ), ` Cmd4PriorityPollingQueue.echoRetryErrors( ${ count } ) echoRetryErrors should be True for count = queueRetryCount = 0` ); count = 1; cmd4PriorityPollingQueue.queueRetryCount = 1; assert.isTrue( cmd4PriorityPollingQueue.echoRetryErrors( count ), ` Cmd4PriorityPollingQueue.echoRetryErrors( ${ count } ) echoRetryErrors should be False for count = queueRetryCount = 1` ); count = 0; cmd4PriorityPollingQueue.queueRetryCount = 1; assert.isFalse( cmd4PriorityPollingQueue.echoRetryErrors( count ), ` Cmd4PriorityPollingQueue.echoRetryErrors( ${ count } ) echoRetryErrors should be False for count < queueRetryCount` ); }); it( "WoRm - Test creation of Default Cmd4PriorityPollingQueue", function( ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let queueName = "Queue A"; cmd4PriorityPollingQueue = new Cmd4PriorityPollingQueue( log, queueName ); expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "cmd4PollingQueues is not an instance of Cmd4PollingQueues" ); assert.equal( "", log.logBuf, ` Cmd4PriorityPollingQueue unexpected stdout received: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` Cmd4PriorityPollingQueue unexpected stderr received: ${ log.errBuf }` ); assert.equal( cmd4PriorityPollingQueue.queueName, queueName, ` Cmd4PriorityPollingQueue.queueName is incorrect` ); assert.isFalse( cmd4PriorityPollingQueue.queueStarted, ` Cmd4PriorityPollingQueue should not be started` ); assert.equal( cmd4PriorityPollingQueue.queueType, constants.DEFAULT_QUEUE_TYPE, ` incorrect default queue type` ); assert.equal( cmd4PriorityPollingQueue.pauseTimer, null, ` incorrect queue pauseTimer ` ); assert.equal( cmd4PriorityPollingQueue.pauseTimerTimeout, constants.DEFAULT_QUEUE_PAUSE_TIMEOUT, ` incorrect queue pauseTimer interval` ); }); it( "WoRm - Test creation of Default Cmd4PriorityPollingQueue from config.json", function( ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "Active" } ], state_cmd: "./test/echoScripts/echo_true_withRcOf1" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); queue.lowPriorityQueue = []; //Make sure no polling happens assert.equal( queue.lowPriorityQueue.length, 0, `Incorrect number of low level polling characteristics` ); Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); queue.pauseTimerTimeout = 0; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( cmd4PriorityPollingQueue.queueType, constants.DEFAULT_QUEUE_TYPE, ` incorrect default queue type. Should be WoRm` ); }); it( "WoRm - Test creation of Default Cmd4PriorityPollingQueue retryCount from config.json", function( ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm", retries: 12 } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "Active" } ], state_cmd: "./test/echoScripts/echo_true_withRcOf1" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); queue.lowPriorityQueue = []; //Make sure no polling happens assert.equal( queue.lowPriorityQueue.length, 0, `Incorrect number of low level polling characteristics` ); Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); queue.pauseTimerTimeout = 0; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( cmd4PriorityPollingQueue.queueType, constants.DEFAULT_QUEUE_TYPE, ` incorrect default queue type. Should be WoRm` ); assert.equal( cmd4PriorityPollingQueue.queueRetryCount, 12, ` incorrect queue retries` ); }); it( "WoRm - Test creation of Default Cmd4PriorityPollingQueue retryCount from config.json", function( ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "Active" } ], state_cmd: "./test/echoScripts/echo_true_withRcOf1" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); queue.lowPriorityQueue = []; //Make sure no polling happens assert.equal( queue.lowPriorityQueue.length, 0, `Incorrect number of low level polling characteristics` ); Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); queue.pauseTimerTimeout = 0; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( cmd4PriorityPollingQueue.queueType, constants.DEFAULT_QUEUE_TYPE, ` incorrect default queue type. Should be WoRm` ); assert.equal( cmd4PriorityPollingQueue.queueRetryCount, constants.DEFAULT_WORM_QUEUE_RETRY_COUNT, ` incorrect default queueRetryCount` ); }); it( "WoRm - Test existance of prioritySetValue", function( ) { let platformConfig = { queueTypes: [ { queue: "7", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, queue: "7", state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = cmd4Accessory.queue; assert.isFunction( cmd4PriorityPollingQueue.prioritySetValue, `.prioritySetValue is not a function` ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "7" with QueueType of: "${ constants.QUEUETYPE_WORM }" retryCount: ${ constants.DEFAULT_WORM_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); }); it( "WoRm - Test existance of priorityGetValue", function( ) { let platformConfig = { queueTypes: [{ queue: "A", queueType: "WoRm" }], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", queue: "A", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = cmd4Accessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "A" with QueueType of: "${ constants.QUEUETYPE_WORM }" retryCount: ${ constants.DEFAULT_WORM_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.isFunction( cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry, `.addLowPriorityGetPolledQueueEntry is not a function` ); assert.isFunction( cmd4PriorityPollingQueue.priorityGetValue, `.priorityGetValue is not a function` ); }); it( "WoRm - Test addLowPriorityGetPolledQueueEntry goes to low priority queue", function( ) { let platformConfig = { queueTypes: [{ queue: "A", queueType: "WoRm" }], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", queue: "A", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = cmd4Accessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.isFunction( cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry, `.addLowPriorityGetPolledQueueEntry is not a function` ); // ( accessory, accTypeEnumIndex, characteristicstring, interval, timeout ) cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4Accessory, CMD4_ACC_TYPE_ENUM.On, "On", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 1, `Polled Get added to low prority queue` ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 0, `Polled Get added to high prority queue` ); let entry = cmd4PriorityPollingQueue.lowPriorityQueue[ 0 ]; assert.equal( entry.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `On was not stored as a get` ); }); it( "WoRm - Test processEntryFromLowPriorityQueue", function( done ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" }], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.On, "On", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.Active, "Active", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 4, `Polled Get added to low prority queue` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); cmd4PriorityPollingQueue.processEntryFromLowPriorityQueue( cmd4PriorityPollingQueue.lowPriorityQueue[ 0 ] ); setTimeout( () => { assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Get 'MySwitch' 'On' timeout: 60000` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: On function for: MySwitch returned: 1` , `expected stdout: ${ log.logBuf }` ); // Low priority queues are continious, make sure it is still the same assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 4, `After poll, low priority queue length should atill be the same size` ); let entry0 = cmd4PriorityPollingQueue.lowPriorityQueue[ 0 ]; let entry1 = cmd4PriorityPollingQueue.lowPriorityQueue[ 1 ]; assert.equal( entry1.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.Active, `After poll, The low priority queue should still be the same` ); assert.equal( entry0.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `After poll, The low priority queue should still be the same` ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueueIndex, 0, `After poll the current index should be 1` ); done( ); }, 1000); }); it('WoRm - Cmd4Platform created pollingQueue.', ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, queue: "A", polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 2, `Incorrect number of created accessories` ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 4, `Incorrect number of low level polling characteristics` ); // ACCESSORY 1 let accessory1 = cmd4Platform.createdCmd4Accessories[0]; expect( accessory1 ).to.be.a.instanceOf( Cmd4Accessory, "accessory1 is not an instance of Cmd4Accessory" ); assert.equal( Object.keys( accessory1.listOfPollingCharacteristics).length, 2, `Incorrect number of polling characteristics for accessory1` ); // ACCESSORY 2 let accessory2 = cmd4Platform.createdCmd4Accessories[1]; expect( accessory2 ).to.be.a.instanceOf( Cmd4Accessory, "accessory2 is not an instance of Cmd4Accessory" ); assert.equal( Object.keys( accessory2.listOfPollingCharacteristics).length, 2, `Incorrect number of polling characteristics for accessory2` ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "A" with QueueType of: "${ constants.QUEUETYPE_WORM }" retryCount: ${ constants.DEFAULT_WORM_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding prioritySetValue for MySwitch characteristic: On` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding priorityGetValue for MySwitch characteristic: On` , `expected stdout: ${ log.logBuf }` ); // There is only one queue created as both accessories are in the same queue let numberOfQueues = Object.keys( settings.listOfCreatedPriorityQueues ).length; assert.equal( numberOfQueues, 1, `Incorrect number of polling queues` ); cmd4Platform.startPolling( 5000, 5000 ); cmd4Platform.pollingTimers.forEach( ( timer ) => { clearTimeout( timer ); }); //let queue = settings.listOfCreatedPriorityQueues[ "A" ]; //expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PriorityPollingQueue" ); done( ); }); it('WoRm - PollingQueue getValue.', ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, queue: "A", polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let numberOfQueues = Object.keys( settings.listOfCreatedPriorityQueues ).length; assert.equal( numberOfQueues, 1, `Incorrect number of polling queues` ); let cmd4PriorityPollingQueue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PriorityPollingQueue" ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 4, `Incorrect number of low priority polled characteristics` ); //let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[1]; cmd4Platform.startPolling( 5000, 5000 ); cmd4Platform.pollingTimers.forEach( ( timer ) => { clearTimeout( timer ); }); // Add IOS Get to start with. This will trigger queue as we had stopped it from starting cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.Active, "Active", constants.DEFAULT_TIMEOUT, dummyCallback ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 5, `Set not added to high prority queue` ); done(); }).timeout(10000); }); describe('QUEUETYPE: STANDARD (Passthru ) - Testing Cmd4PriorityPollingQueue polling', ( ) => { // So we can cancel any timers let cmd4PriorityPollingQueue; before( ( ) => { sinon.stub( process, `exit` ); cleanStatesDir(); }); after( ( ) => { process.exit.restore( ); }); beforeEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; // MaxListenersExceededWarning: Possible EventEmitter memory leak detected _api.removeAllListeners(); }); it( "Standard - Test if Cmd4PriorityPollingQueue exists", function ( ) { expect( Cmd4PriorityPollingQueue ).not.to.be.a( "null", "Cmd4PriorityPollingQueue was null" ); }); it( "Standard - Test creation of Standard ( Passthrue ) Cmd4PriorityPollingQueue", function( ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let queueName = "Queue A"; cmd4PriorityPollingQueue = new Cmd4PriorityPollingQueue( log, queueName, constants.QUEUETYPE_STANDARD ); expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "cmd4PollingQueues is not an instance of Cmd4PollingQueues" ); assert.equal( "", log.logBuf, ` Cmd4PriorityPollingQueue unexpected stdout received: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` Cmd4PriorityPollingQueue unexpected stderr received: ${ log.errBuf }` ); assert.equal( cmd4PriorityPollingQueue.queueName, queueName, ` Cmd4PriorityPollingQueue.queueName is incorrect` ); assert.isFalse( cmd4PriorityPollingQueue.queueStarted, ` Cmd4PriorityPollingQueue should not be started` ); assert.equal( cmd4PriorityPollingQueue.queueType, constants.QUEUETYPE_STANDARD, ` incorrect default queue type` ); assert.equal( cmd4PriorityPollingQueue.pauseTimer, null, ` incorrect queue pauseTimer ` ); assert.equal( cmd4PriorityPollingQueue.pauseTimerTimeout, constants.DEFAULT_QUEUE_PAUSE_TIMEOUT, ` incorrect queue pauseTimer interval` ); }); it( "Standard - Test existance of prioritySetValue", function( ) { let platformConfig = { accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = cmd4Accessory.queue; assert.isFunction( cmd4PriorityPollingQueue.prioritySetValue, `.prioritySetValue is not a function` ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MySwitch" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); }); it( "Standard - Test existance of priorityGetValue", function( ) { let platformConfig = { accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = cmd4Accessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MySwitch" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.isFunction( cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry, `.addLowPriorityGetPolledQueueEntry is not a function` ); assert.isFunction( cmd4PriorityPollingQueue.priorityGetValue, `.priorityGetValue is not a function` ); }); it( "Standard - Test addLowPriorityGetPolledQueueEntry goes to low priority queue", function( ) { let platformConfig = { accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = cmd4Accessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MySwitch" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.isFunction( cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry, `.addLowPriorityGetPolledQueueEntry is not a function` ); // ( accessory, accTypeEnumIndex, characteristicstring, interval, timeout ) cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4Accessory, CMD4_ACC_TYPE_ENUM.On, "On", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 1, `Polled Get added to low prority queue` ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 0, `Polled Get added to high prority queue` ); let entry = cmd4PriorityPollingQueue.lowPriorityQueue[ 0 ]; assert.equal( entry.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `On was not stored as a get` ); }); it( "Standard - Test processEntryFromLowPriorityQueue", function( done ) { let platformConfig = { accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, polling: [ { characteristic: "on" }, { characteristic: "active" }], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MySwitch" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.On, "On", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.Active, "Active", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 4, `Polled Get added to low prority queue` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); cmd4PriorityPollingQueue.processEntryFromLowPriorityQueue( cmd4PriorityPollingQueue.lowPriorityQueue[ 0 ] ); setTimeout( () => { assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Get 'MySwitch' 'On' timeout: 60000` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: On function for: MySwitch returned: 1` , `expected stdout: ${ log.logBuf }` ); // Low priority queues are continious, make sure it is still the same assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 4, `After poll, low priority queue length should atill be the same size` ); let entry0 = cmd4PriorityPollingQueue.lowPriorityQueue[ 0 ]; let entry1 = cmd4PriorityPollingQueue.lowPriorityQueue[ 1 ]; assert.equal( entry1.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.Active, `After poll, The low priority queue should still be the same` ); assert.equal( entry0.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `After poll, The low priority queue should still be the same` ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueueIndex, 0, `After poll the current index should be 1` ); done( ); }, 1000); }); it('Standard - Cmd4Platform created pollingQueue.', ( done ) => { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "on", Interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( cmd4Platform.createdCmd4Accessories.length, 2, `Incorrect number of created accessories` ); // There should be two queues created, one for each accessory assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 2, `Incorrect number of polling queues created` ); // ACCESSORY 1 let accessory1 = cmd4Platform.createdCmd4Accessories[0]; let queue1 = accessory1.queue; expect( accessory1 ).to.be.a.instanceOf( Cmd4Accessory, "accessory1 is not an instance of Cmd4Accessory" ); expect( queue1 ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue1 is not an instance of Cmd4PollingQueue" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MyLight" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.equal( queue1.lowPriorityQueue.length, 2 , `Incorrect number of low level polling characteristics` ); assert.equal( Object.keys( accessory1.listOfPollingCharacteristics).length, 2, `Incorrect number of polling characteristics for accessory1` ); assert.include( log.logBuf, `Adding prioritySetValue for MyLight characteristic: On` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding priorityGetValue for MyLight characteristic: On` , `expected stdout: ${ log.logBuf }` ); // ACCESSORY 2 let accessory2 = cmd4Platform.createdCmd4Accessories[1]; expect( accessory2 ).to.be.a.instanceOf( Cmd4Accessory, "accessory2 is not an instance of Cmd4Accessory" ); let queue2 = accessory2.queue; expect( queue2 ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue2 is not an instance of Cmd4PollingQueue" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MySwitch" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.equal( queue2.lowPriorityQueue.length, 2 , `Incorrect number of low level polling characteristics` ); assert.equal( Object.keys( accessory2.listOfPollingCharacteristics).length, 2, `Incorrect number of polling characteristics for accessory2` ); assert.include( log.logBuf, `Adding prioritySetValue for MySwitch characteristic: On` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding priorityGetValue for MySwitch characteristic: On` , `expected stdout: ${ log.logBuf }` ); cmd4Platform.startPolling( 5000, 5000 ); cmd4Platform.pollingTimers.forEach( ( timer ) => { clearTimeout( timer ); }); //let queue = settings.listOfCreatedPriorityQueues[ "A" ]; //expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PriorityPollingQueue" ); done( ); }); it('Standard - PollingQueue getValue.', ( done ) => { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); let numberOfQueues = Object.keys( settings.listOfCreatedPriorityQueues ).length; assert.equal( numberOfQueues, 2, `Incorrect number of polling queues` ); // ACCESSORY 1 let accessory1 = cmd4Platform.createdCmd4Accessories[0]; let queue1 = accessory1.queue; expect( accessory1 ).to.be.a.instanceOf( Cmd4Accessory, "accessory1 is not an instance of Cmd4Accessory" ); expect( queue1 ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue1 is not an instance of Cmd4PollingQueue" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MyLight" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.equal( queue1.lowPriorityQueue.length, 2 , `Incorrect number of low level polling characteristics` ); assert.equal( Object.keys( accessory1.listOfPollingCharacteristics).length, 2, `Incorrect number of polling characteristics for accessory1` ); assert.include( log.logBuf, `Adding prioritySetValue for MyLight characteristic: On` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding priorityGetValue for MyLight characteristic: On` , `expected stdout: ${ log.logBuf }` ); // ACCESSORY 2 let accessory2 = cmd4Platform.createdCmd4Accessories[1]; expect( accessory2 ).to.be.a.instanceOf( Cmd4Accessory, "accessory2 is not an instance of Cmd4Accessory" ); let queue2 = accessory2.queue; expect( queue2 ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue2 is not an instance of Cmd4PollingQueue" ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MySwitch" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); assert.equal( queue2.lowPriorityQueue.length, 2 , `Incorrect number of low level polling characteristics` ); assert.equal( Object.keys( accessory2.listOfPollingCharacteristics).length, 2, `Incorrect number of polling characteristics for accessory2` ); assert.include( log.logBuf, `Adding prioritySetValue for MySwitch characteristic: On` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `Adding priorityGetValue for MySwitch characteristic: On` , `expected stdout: ${ log.logBuf }` ); //let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[1]; cmd4Platform.startPolling( 5000, 5000 ); cmd4Platform.pollingTimers.forEach( ( timer ) => { clearTimeout( timer ); }); // Add IOS Get to start with. This will trigger queue as we had stopped it from starting cmd4SwitchAccessory.queue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.Active, "Active", constants.DEFAULT_TIMEOUT, dummyCallback ); assert.equal( cmd4SwitchAccessory.queue.lowPriorityQueue.length, 3, `Set not added to high prority queue` ); done(); }).timeout(10000); }); describe('WoRM - Testing Cmd4PriorityPollingQueue recovery correction', ( ) => { // So we can cancel any timers let cmd4PriorityPollingQueue; before( ( ) => { sinon.stub( process, `exit` ); cleanStatesDir(); }); after( ( ) => { process.exit.restore( ); }); beforeEach( function( ) { settings.arrayOfAllStaggeredPollingCharacteristics = [ ]; settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; // MaxListenersExceededWarning: Possible EventEmitter memory leak detected _api.removeAllListeners(); }); it( `WoRM - Test Cmd4PriorityPollingQueue queue can be started`, function( done ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let queueName = "Queue A"; cmd4PriorityPollingQueue = new Cmd4PriorityPollingQueue( log, queueName ); expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "cmd4PollingQueues is not an instance of Cmd4PollingQueues" ); var allDoneCallback = function( allDoneCount ) { assert.equal( allDoneCount, 0, ` startQueue incorrect allDoneCount` ); log.debug( "Test Cmd4PriorityQueue startQueue - allDone called" ); }; cmd4PriorityPollingQueue.startQueue( cmd4PriorityPollingQueue, allDoneCallback ); assert.include( log.logBuf, `[90menablePolling for the first time`, `Cmd4PriorityPollingQueue expected stdout received: ${ log.logBuf }` ); assert.equal( log.errBuf, ``, ` Cmd4PriorityPollingQueue Unexpected stderr received: ${ log.errBuf }` ); done( ); }); it( `WoRM - Test Cmd4PriorityPollingQueue adds an entry to the highPriorityQueue`, function( done ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, queue: "A", polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[33mAdding prioritySetValue for MyLight characteristic: On`, `Cmd4PriorityPollingQueue expected stdout received: ${ log.logBuf }` ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.isFunction( cmd4PriorityPollingQueue.prioritySetValue, `.prioritySetValue is not a function` ); // Set to a large amount so it does not happen cmd4PriorityPollingQueue.recoveryTimerInterval = 500000; // Fake the queue to be blocked cmd4PriorityPollingQueue.inProgressSets = 1; // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Brightness ] .characteristic ).setValue( 22, dummyCallback ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Polled Get added to low prority queue` ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 1, `Polled Get added to high prority queue` ); let entry = cmd4PriorityPollingQueue.highPriorityQueue[ 0 ]; assert.equal( entry.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.Brightness, `Incorrect accTypeEnumIndex in entry` ); assert.equal( entry.isSet, true, `Incorrect isSet in entry` ); assert.equal( entry.characteristicString, "Brightness", `Incorrect isSet in entry` ); assert.equal( entry.value, 22, `Incorrect value in entry` ); done(); }); it( `WoRM - Test Cmd4PriorityPollingQueue adds multiple entries to the highPriorityQueue`, function( done ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, queue: "A", polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[33mAdding prioritySetValue for MyLight characteristic: On`, `Cmd4PriorityPollingQueue expected stdout received: ${ log.logBuf }` ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.isFunction( cmd4PriorityPollingQueue.prioritySetValue, `.prioritySetValue is not a function` ); // Set to a large amount so it does not happen cmd4PriorityPollingQueue.recoveryTimerInterval = 500000; // Fake the queue to be blocked cmd4PriorityPollingQueue.inProgressSets = 1; // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Brightness ] .characteristic ).setValue( 22, dummyCallback ); // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ] .characteristic ).setValue( 0, dummyCallback ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Polled Get added to low prority queue` ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 2, `Polled Get added to high prority queue` ); let entry = cmd4PriorityPollingQueue.highPriorityQueue[ 1 ]; assert.equal( entry.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `Incorrect accTypeEnumIndex in entry` ); assert.equal( entry.isSet, true, `Incorrect isSet in entry` ); assert.equal( entry.characteristicString, "On", `Incorrect isSet in entry` ); assert.equal( entry.value, 0, `Incorrect value in entry` ); done(); }); it( `WoRM - Test Cmd4PriorityPollingQueue Adds "Set" after existing "set" in queue`, function( done ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, queue: "A", polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[33mAdding prioritySetValue for MyLight characteristic: On`, `Cmd4PriorityPollingQueue expected stdout received: ${ log.logBuf }` ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.isFunction( cmd4PriorityPollingQueue.prioritySetValue, `.prioritySetValue is not a function` ); // Set to a large amount so it does not happen cmd4PriorityPollingQueue.recoveryTimerInterval = 500000; // Fake the queue to be blocked cmd4PriorityPollingQueue.inProgressSets = 1; // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Brightness ] .characteristic ).setValue( 22, dummyCallback ); // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ] .characteristic ).setValue( 1, dummyCallback ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Polled Get added to low prority queue` ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 2, `Polled Get added to high prority queue` ); let entry = cmd4PriorityPollingQueue.highPriorityQueue[ 1 ]; assert.equal( entry.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `Incorrect accTypeEnumIndex in entry` ); assert.equal( entry.isSet, true, `Incorrect isSet in entry` ); assert.equal( entry.characteristicString, "On", `Incorrect isSet in entry` ); assert.equal( entry.value, true, `Incorrect value in entry` ); done(); }); it( `V2 crippled WoRM - Test Cmd4PriorityPollingQueue Adds "Get" after existing "set" in queue`, function( done ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, queue: "A", polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[33mAdding prioritySetValue for MyLight characteristic: On`, `Cmd4PriorityPollingQueue expected stdout received: ${ log.logBuf }` ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.isFunction( cmd4PriorityPollingQueue.prioritySetValue, `.prioritySetValue is not a function` ); // Set to a large amount so it does not happen cmd4PriorityPollingQueue.recoveryTimerInterval = 500000; // Fake the queue to be blocked cmd4PriorityPollingQueue.inProgressSets = 1; // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Brightness ] .characteristic ).setValue( 22, dummyCallback ); /* Characteristic.getValue() is deprecated in V2 and Characteristic.value does not call the * get functions. Worked last in homebridge-1.8 // Call the getValue bound function, which is priorityGetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ] .characteristic ).getValue( dummyCallback ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Polled Get added to low prority queue` ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 2, `Polled Get added to high prority queue` ); let entry = cmd4PriorityPollingQueue.highPriorityQueue[ 1 ]; // Even though the "Get" was added second, the next "Set" gets put before it assert.equal( entry.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `Incorrect accTypeEnumIndex in entry` ); assert.equal( entry.isSet, false, `Incorrect isSet in entry` ); assert.equal( entry.characteristicString, "On", `Incorrect isSet in entry` ); */ done(); }); it( `V2 crippled WoRM - Test Cmd4PriorityPollingQueue same "Set" replaces old in queue`, function( done ) { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, queue: "A", polling: [ { characteristic: "on", interval: 310 }, { characteristic: "brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[33mAdding prioritySetValue for MyLight characteristic: On`, `Cmd4PriorityPollingQueue expected stdout received: ${ log.logBuf }` ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.isFunction( cmd4PriorityPollingQueue.prioritySetValue, `.prioritySetValue is not a function` ); // Set to a large amount so it does not happen cmd4PriorityPollingQueue.recoveryTimerInterval = 5000000; // Fake the queue to be blocked cmd4PriorityPollingQueue.inProgressSets = 1; // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Brightness ] .characteristic ).setValue( 22, dummyCallback ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 1, `Polled Get added to high prority queue` ); /* Characteristic.getValue() is deprecated in V2 and Characteristic.value does not call the * get functions. Worked last in homebridge-1.8 // Call the getValue bound function, which is priorityGetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ] .characteristic ).getValue( dummyCallback ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 2, `Polled Get added to high prority queue` ); // Call the setValue bound function, which is prioritySetValue cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ] .characteristic ).setValue( 1, dummyCallback ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 3, `Polled Get added to high prority queue` ); // This setValue should replace the first cmd4LightAccessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.Brightness ] .characteristic ).setValue( 30, dummyCallback ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Polled Get added to low prority queue` ); assert.equal( cmd4PriorityPollingQueue.highPriorityQueue.length, 3, `Polled Get added to high prority queue` ); let entry = cmd4PriorityPollingQueue.highPriorityQueue[ 0 ]; // Check that the entry was replaced at queue index 0. assert.equal( entry.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.Brightness, `Incorrect accTypeEnumIndex in entry` ); assert.equal( entry.isSet, true, `Incorrect isSet in entry` ); assert.equal( entry.characteristicString, "Brightness", `Incorrect isSet in entry` ); assert.equal( entry.value, 30, `Incorrect value in entry` ); */ done(); }); it( `WoRM - Test "Get" Entry From High Priority Queue`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: cmd4SwitchAccessory.hV.timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Get" entry: ${ CMD4_ACC_TYPE_ENUM.On } isUpdate: false length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Get 'MySwitch' 'On' timeout: 60000` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: On function for: MySwitch returned: 1` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); it( `WoRM - Test "Get" Entry From High Priority Queue Failure >` + constants.DEFAULT_WORM_QUEUE_RETRY_COUNT + ` times`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "./test/echoScripts/echo_true_withRcOf1" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); queue.lowPriorityQueue = []; //Make sure no polling happens assert.equal( queue.lowPriorityQueue.length, 0, `Incorrect number of low level polling characteristics` ); Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); queue.pauseTimerTimeout = 0; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; var allDoneCallback = function( allDoneCount ) { assert.equal( allDoneCount, 0, ` startQueue incorrect allDoneCount` ); log.debug( "Test Cmd4PriorityQueue startQueue - allDone called" ); }; assert.equal( cmd4PriorityPollingQueue.queueType, constants.DEFAULT_QUEUE_TYPE, ` incorrect default queue type. Should be WoRm` ); cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: dummyCallback } ); //cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); cmd4PriorityPollingQueue.startQueue( cmd4PriorityPollingQueue, allDoneCallback ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.errBuf, `[33m*1* error(s) were encountered for "MySwitch" getValue. Last error found Getting: "On". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); // A quick way to stop the queue. For whatever reason, if the above fails, // the testcase will not do this command and the testcase runs forever cmd4SwitchAccessory.queue.inProgressSets = 10; done( ); }, 1500 ); }).timeout( 2000 ); it( `WoRM - Test "Get" Entry RetryCount = 1 fails twice`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm", retries: 1 } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", timeout: 10, // Need to change the record we add for this polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "./test/echoScripts/runToTimeoutRcOf1" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.queueRetryCount, 1, `Incorrect queueRetryCount` ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); queue.lowPriorityQueue = []; //Make sure no polling happens assert.equal( queue.lowPriorityQueue.length, 0, `Incorrect number of low level polling characteristics` ); Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); queue.pauseTimerTimeout = 0; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; var allDoneCallback = function( allDoneCount ) { assert.equal( allDoneCount, 0, ` startQueue incorrect allDoneCount` ); log.debug( "Test Cmd4PriorityQueue startQueue - allDone called" ); }; assert.equal( cmd4PriorityPollingQueue.queueType, constants.DEFAULT_QUEUE_TYPE, ` incorrect default queue type. Should be WoRm` ); cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: 10, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: dummyCallback } ); //cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); cmd4PriorityPollingQueue.startQueue( cmd4PriorityPollingQueue, allDoneCallback ); // Wait for the 1st entry in the Queue to be processed setTimeout( ( ) => { assert.include( log.errBuf, `[33m*2* error(s) were encountered for "MySwitch" getValue. Last error found Getting: "On". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); assert.equal( cmd4PriorityPollingQueue.queueRetryCount, 1, `incorrect queueReturyCount. Should be 1` ); // A quick way to stop the queue. For whatever reason, if the above fails, // the testcase will not do this command and the testcase runs forever cmd4SwitchAccessory.queue.inProgressSets = 10; done( ); }, 2500 ); }).timeout( 3000 ); it( `WoRM - Test "Set" Entry From High Priority Queue`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue setValue will just return successful // and do the actual setValue later var dummyCallback = function( rc ) { assert.equal( rc, 0, ` setValue incorrect rc: ${ rc }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: true, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Set" entry: ${ CMD4_ACC_TYPE_ENUM.On } length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90msetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch 1 cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Set 'MySwitch' 'On' '1'` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); it( `WoRM - Test "Set" Entry From High Priority Queue Failure >*` + constants.DEFAULT_WORM_QUEUE_RETRY_COUNT + ` times`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "./test/echoScripts/echo_true_withRcOf1" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.queueType, constants.DEFAULT_QUEUE_TYPE, `Incorrect queue type was created ` ); assert.equal( queue.queueRetryCount, constants.DEFAULT_WORM_QUEUE_RETRY_COUNT, `Incorrect queue retryCount ` ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); queue.lowPriorityQueue = []; //Make sure no polling happens assert.equal( queue.lowPriorityQueue.length, 0, `Incorrect number of low level polling characteristics` ); Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); queue.pauseTimerTimeout = 0; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; var allDoneCallback = function( allDoneCount ) { assert.equal( allDoneCount, 0, ` startQueue incorrect allDoneCount` ); log.debug( "Test Cmd4PriorityQueue startQueue - allDone called" ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: true, [ constants.CALLBACK_lv ]: dummyCallback } ); //cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); cmd4PriorityPollingQueue.startQueue( cmd4PriorityPollingQueue, allDoneCallback ); // Wait for the Queue to be processed setTimeout( ( ) => { // Counting starts from zero, i.e queueRetries = 0, so add 1 assert.include( log.errBuf, `[33m*${ constants.DEFAULT_WORM_QUEUE_RETRY_COUNT +1 }* error(s) were encountered for "MySwitch" getValue. Last error found Getting: "On". Perhaps you should run in debug mode to find out what the problem might be.\u001b`, `queue Incorrect stderr: ${ log.errBuf }` ); // A quick way to stop the queue. For whatever reason, if the above fails, // the testcase will not do this command and the testcase runs forever cmd4SwitchAccessory.queue.inProgressSets = 10; done( ); }, 1500 ); }).timeout( 2000 ); it( `WoRM - Test "Set" Entry With Related CharacteristicFrom High Priority Queue`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: "STOPPED", stateChangeResponseTime: 0, queue: "A", polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4DoorAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4DoorAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let dummyCallbackCount = 0; var dummyCallback = function( rc ) { dummyCallbackCount ++; assert.equal( dummyCallbackCount, 1, ` callback called too many times` ); assert.equal( rc, 0, ` setValue incorrect rc: ${ rc }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4DoorAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.TargetPosition, [ constants.CHARACTERISTIC_STRING_lv ]: "TargetPosition", [ constants.TIMEOUT_lv ]: cmd4DoorAccessory.timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: 100, [ constants.CALLBACK_lv ]: dummyCallback } ); //log.reset( ); //log.setOutputEnabled( false ); //log.setDebugEnabled( true ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_SET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Set" entry: ${ CMD4_ACC_TYPE_ENUM.TargetPosition} length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90msetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.TargetPosition } )-"TargetPosition" function for: MyDoor 100 cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Set 'MyDoor' 'TargetPosition' '100'`, `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mProcessing high priority queue "Get" entry: 43 isUpdate: true length: ` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.CurrentPosition } )-"CurrentPosition" function for: MyDoor cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Get 'MyDoor' 'CurrentPosition'`, `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: CurrentPosition function for: MyDoor returned: 100`, `expected stdout: ${ log.logBuf }` ); done( ); }, 3000 ); }).timeout( 6000 ); }); describe('Standard - Testing Cmd4PriorityPollingQueue recovery correction', ( ) => { // So we can cancel any timers let cmd4PriorityPollingQueue; before( ( ) => { sinon.stub( process, `exit` ); cleanStatesDir(); }); after( ( ) => { process.exit.restore( ); }); beforeEach( function( ) { settings.arrayOfAllStaggeredPollingCharacteristics = [ ]; settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; }); it( `Standard - Test Cmd4PriorityPollingQueue queue can be started`, function( done ) { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let queueName = "Queue A"; cmd4PriorityPollingQueue = new Cmd4PriorityPollingQueue( log, queueName, constants.QUEUETYPE_STANDARD ); expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "cmd4PollingQueues is not an instance of Cmd4PollingQueues" ); var allDoneCallback = function( allDoneCount ) { assert.equal( allDoneCount, 0, ` startQueue incorrect allDoneCount` ); log.debug( "Test Cmd4PriorityQueue startQueue - allDone called" ); }; cmd4PriorityPollingQueue.startQueue( cmd4PriorityPollingQueue, allDoneCallback ); assert.include( log.logBuf, `[90menablePolling for the first time`, `Cmd4PriorityPollingQueue expected stdout received: ${ log.logBuf }` ); assert.equal( log.errBuf, ``, ` Cmd4PriorityPollingQueue Unexpected stderr received: ${ log.errBuf }` ); done( ); }); it( `Standard - Test "Get" Entry From High Priority Queue`, ( done ) => { let platformConfig = { accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); assert.include( log.logBuf, `[90mCreating new Priority Polled Queue "Q:MySwitch" with QueueType of: "${ constants.QUEUETYPE_STANDARD }" retryCount: ${ constants.DEFAULT_STANDARD_QUEUE_RETRY_COUNT }` , `expected stdout: ${ log.logBuf }` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4SwitchAccessory ).to.be.a.instanceOf( Cmd4Accessory, `cmd4Platform did not create an instance of Cmd4Accessory` ); let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: cmd4SwitchAccessory.hV.timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Get" entry: ${ CMD4_ACC_TYPE_ENUM.On } isUpdate: false length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Get 'MySwitch' 'On' timeout: 60000` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: On function for: MySwitch returned: 1` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); it( `Standard - Test "Set" Entry From High Priority Queue`, ( done ) => { let platformConfig = { accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue setValue will just return successful // and do the actual setValue later var dummyCallback = function( rc ) { assert.equal( rc, 0, ` setValue incorrect rc: ${ rc }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: true, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Set" entry: ${ CMD4_ACC_TYPE_ENUM.On } length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90msetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch 1 cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Set 'MySwitch' 'On' '1'` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); it( `Standard - Test "Set" Entry With Related CharacteristicFrom High Priority Queue`, ( done ) => { let platformConfig = { accessories: [ { name: "MyDoor", displayName: "MyDoor", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: "STOPPED", stateChangeResponseTime: 0, polling: [ { characteristic: "currentPosition" }, { characteristic: "targetPosition" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let cmd4DoorAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4DoorAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let dummyCallbackCount = 0; var dummyCallback = function( rc ) { dummyCallbackCount ++; assert.equal( dummyCallbackCount, 1, ` callback called too many times` ); assert.equal( rc, 0, ` setValue incorrect rc: ${ rc }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4DoorAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.TargetPosition, [ constants.CHARACTERISTIC_STRING_lv ]: "TargetPosition", [ constants.TIMEOUT_lv ]: cmd4DoorAccessory.timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: 100, [ constants.CALLBACK_lv ]: dummyCallback } ); //log.reset( ); //log.setOutputEnabled( false ); //log.setDebugEnabled( true ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_SET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Set" entry: ${ CMD4_ACC_TYPE_ENUM.TargetPosition } length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90msetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.TargetPosition } )-"TargetPosition" function for: MyDoor 100 cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Set 'MyDoor' 'TargetPosition' '100'`, `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mProcessing high priority queue "Get" entry: ${ CMD4_ACC_TYPE_ENUM.CurrentPosition } isUpdate: true length: ` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.CurrentPosition } )-"CurrentPosition" function for: MyDoor cmd: node ./Extras/Cmd4Scripts/Examples/AnyDevice Get 'MyDoor' 'CurrentPosition'`, `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: CurrentPosition function for: MyDoor returned: 100`, `expected stdout: ${ log.logBuf }` ); done( ); }, 3000 ); }).timeout( 6000 ); }); ================================================ FILE: test/Cmd4Storage.js ================================================ "use strict"; let Cmd4Storage = require( "../utils/Cmd4Storage" ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); describe(`Basic Cmd4Storage Tests`, ( ) => { it( `Test creation of Cmd4Storage`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); const cmd4Storage = new Cmd4Storage( log ); assert.instanceOf( cmd4Storage , Cmd4Storage, "Expected cmd4Storage to be instance of Cmd4Storage. Found %s" , cmd4Storage ); assert.isFunction( cmd4Storage.getStoredValueForIndex, ".getStoredValueForIndex is not a function" ); assert.isFunction( cmd4Storage.getStoredValueForCharacteristic, ".getStoredValueForCharacteristic is not a function" ); assert.isFunction( cmd4Storage.setStoredValueForIndex, ".setStoredValueForIndex is not a function" ); assert.isFunction( cmd4Storage.setStoredValueForCharacteristic, ".setStoredValueForCharacteristic is not a function" ); assert.isFunction( cmd4Storage.testStoredValueForIndex, ".testStoredValueForIndex is not a function" ); assert.isFunction( cmd4Storage.testStoredValueForCharacteristic, ".testStoredValueForCharacteristic is not a function" ); assert.isFunction( cmd4Storage.loadLatestData, ".loadLatestData is not a function" ); done( ); }); it( `Cmd4Storage can init itself properly`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let cmd4Storage = new Cmd4Storage( log ); assert.equal( cmd4Storage.DATA.length, CMD4_ACC_TYPE_ENUM.EOL, `cmd4Storage is not the correct size` ); for ( let i = 0; i < CMD4_ACC_TYPE_ENUM.EOL; i++ ) { assert.equal( cmd4Storage.DATA[ i ], null, `cmd4Storage[ ${ i } ] is not null` ); } done( ); }); it( `Cmd4Storage can set data properly using an index`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, 1 ); assert.equal( cmd4Storage.DATA[ CMD4_ACC_TYPE_ENUM.On ], 1, `cmd4Storage.DATA[ ${ CMD4_ACC_TYPE_ENUM.On } ] is not 1` ); done( ); }); it( `Cmd4Storage can set data properly using a UC characteristic`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForCharacteristic( characteristicString, 1 ); assert.equal( cmd4Storage.DATA[ CMD4_ACC_TYPE_ENUM.On ], 1, `cmd4Storage.DATA[ ${ CMD4_ACC_TYPE_ENUM.On } ] is not 1` ); done( ); }); it( `Cmd4Storage can set data properly using a LC characteristic`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForCharacteristic( characteristicString, 1 ); assert.equal( cmd4Storage.DATA[ CMD4_ACC_TYPE_ENUM.On ], 1, `cmd4Storage.DATA[ ${ CMD4_ACC_TYPE_ENUM.On } ] is not 1` ); done( ); }); it( `Cmd4Storage can get data properly using an index`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, 1 ); assert.equal( cmd4Storage.DATA[ CMD4_ACC_TYPE_ENUM.On ], 1, `cmd4Storage.DATA[ ${ CMD4_ACC_TYPE_ENUM.On } ] is not 1` ); let value = cmd4Storage.getStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On ); assert.equal( value, 1, `cmd4Storage.getStoredValueForIndex( ${ CMD4_ACC_TYPE_ENUM.On } ) did not return 1` ); done( ); }); it( `Cmd4Storage can get data properly using a LC characteristic`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, 1 ); assert.equal( cmd4Storage.DATA[ CMD4_ACC_TYPE_ENUM.On ], 1, `cmd4Storage.DATA[ ${ CMD4_ACC_TYPE_ENUM.On } ] is not 1` ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let value = cmd4Storage.getStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage.getStoredValueForCharacteristic( ${ characteristicString } ) did not return 1` ); done( ); }); it( `Cmd4Storage can get data properly using a UC characteristic`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, 1 ); assert.equal( cmd4Storage.DATA[ CMD4_ACC_TYPE_ENUM.On ], 1, `cmd4Storage.DATA[ ${ CMD4_ACC_TYPE_ENUM.On } ] is not 1` ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let value = cmd4Storage.getStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage.getStoredValueForCharacteristic( ${ characteristicString } ) did not return 1` ); done( ); }); it( `Cmd4Storage can test data properly for an index value to be null`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); //let characteristicString = CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ].type; let cmd4Storage = new Cmd4Storage( log ); let value = cmd4Storage.testStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On ); assert.equal( value, undefined, `cmd4Storage.testSoredValueForIndex( ${ CMD4_ACC_TYPE_ENUM.On } ) did not return undefined` ); done( ); }); it( `Cmd4Storage can test data properly for an index value to be set`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, 1 ); let value = cmd4Storage.testStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On ); assert.equal( value, 1, `cmd4Storage.testSoredValueForIndex( ${ CMD4_ACC_TYPE_ENUM.On } ) did not return 1` ); done( ); }); it( `Cmd4Storage can test data properly for an UC characteristic value to be null`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); let cmd4Storage = new Cmd4Storage( log ); let value = cmd4Storage.testStoredValueForCharacteristic( characteristicString ); assert.equal( value, undefined, `cmd4Storage.testSoredValueForIndex( ${ characteristicString } ) did not return undefined` ); done( ); }); it( `Cmd4Storage can test data properly for an LC characteristic value to be null`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let cmd4Storage = new Cmd4Storage( log ); let value = cmd4Storage.testStoredValueForCharacteristic( characteristicString ); assert.equal( value, undefined, `cmd4Storage.testSoredValueForIndex( ${ characteristicString } ) did not return undefined` ); done( ); }); it( `Cmd4Storage can test data properly for an UC characteristic value to be set`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, 1 ); let value = cmd4Storage.testStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage.testSoredValueForIndex( ${ CMD4_ACC_TYPE_ENUM.On } ) did not return 1` ); done( ); }); it( `Cmd4Storage can test data properly for an LC characteristic value to be set`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForIndex( CMD4_ACC_TYPE_ENUM.On, 1 ); let value = cmd4Storage.testStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage.testSoredValueForIndex( ${ CMD4_ACC_TYPE_ENUM.On } ) did not return 1` ); done( ); }); it( `Cmd4Storage can set/get data`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let cmd4Storage = new Cmd4Storage( log ); cmd4Storage.setStoredValueForCharacteristic( characteristicString, 50 ); let rc = cmd4Storage.getStoredValueForCharacteristic( characteristicString ); assert.equal( rc, 50, `cmd4Storage did not set/get data correctly` ); done( ); }); }); describe(`Init with Class data Tests`, ( ) => { it( `Test creation of Cmd4Storage with Class data`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); // The Test data let testData = new Cmd4Storage( log ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); testData.setStoredValueForCharacteristic( characteristicString, 1 ); let cmd4Storage = new Cmd4Storage( log, testData ); assert.instanceOf( cmd4Storage , Cmd4Storage, "Expected cmd4Storage to be instance of Cmd4Storage. Found %s" , cmd4Storage ); let value = cmd4Storage.getStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage[ ${characteristicString} ] did not return 1` ); done( ); }); it( `Test creation of Cmd4Storage with invalid Class data throws error`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); // The Test data let testData = new Cmd4Storage( log ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); testData.setStoredValueForCharacteristic( characteristicString, 1 ); expect ( ( ) => new Cmd4Storage( log, 155 ) ).to.throw(/Do not know how to handle typeof: number Cmd4_Storage parm: 155/); done( ); }); it( `Test creation of Cmd4Storage with invalid Class Version throws error`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); // The Test data let testData = new Cmd4Storage( log ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); testData.setStoredValueForCharacteristic( characteristicString, 1 ); testData.CLASS_VERSION = 0; expect ( ( ) => new Cmd4Storage( log, testData ) ).to.throw(/Do not know how to handle Cmd4_Storage Class version: 0/); done( ); }); }); describe(`Init with Old data Tests`, ( ) => { it( `Test creation of Cmd4Storage with old data below ListPairing`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); // The Test data let storedValuesPerCharacteristic = new Array( CMD4_ACC_TYPE_ENUM.EOL -1 ).fill( null ); storedValuesPerCharacteristic[ CMD4_ACC_TYPE_ENUM.Active ] = 1; let cmd4Storage = new Cmd4Storage( log, storedValuesPerCharacteristic ); assert.instanceOf( cmd4Storage , Cmd4Storage, "Expected cmd4Storage to be instance of Cmd4Storage. Found %s" , cmd4Storage ); let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.Active ); let value = cmd4Storage.getStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage[ ${characteristicString} ] did not return 1` ); done( ); }); it( `Test creation of Cmd4Storage with old data above ListPairing`, ( done ) => { let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); // The Test data let storedValuesPerCharacteristic = new Array( CMD4_ACC_TYPE_ENUM.EOL -1 ).fill( null ); // The old data would be the new minus one. storedValuesPerCharacteristic[ CMD4_ACC_TYPE_ENUM.LockControlPoint -1 ] = 1; storedValuesPerCharacteristic[ CMD4_ACC_TYPE_ENUM.WiFiSatelliteStatus -1 ] = 1; let cmd4Storage = new Cmd4Storage( log, storedValuesPerCharacteristic ); assert.instanceOf( cmd4Storage , Cmd4Storage, "Expected cmd4Storage to be instance of Cmd4Storage. Found %s" , cmd4Storage ); // Check the next characteristic past ListPairing let characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.LockControlPoint ); let value = cmd4Storage.getStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage[ ${characteristicString} ] did not return 1` ); characteristicString = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.WiFiSatelliteStatus ); value = cmd4Storage.getStoredValueForCharacteristic( characteristicString ); assert.equal( value, 1, `cmd4Storage[ ${characteristicString} ] did not return 1` ); done( ); }); }); ================================================ FILE: test/HV.js ================================================ "use strict"; let HV = require( "../utils/HV" ); let constants = require( "../cmd4Constants" ); describe(`A Hierarchy Variable Test`, ( ) => { it( `Test creation of HV`, ( ) => { const hv = new HV( ); assert.instanceOf( hv , HV, "Expected hv to be instance of HV. Found %s" , hv ); assert.isFunction( hv.update, ".update is not a function" ); assert.equal( hv.allowTLV8, constants.DEFAULT_ALLOW_TLV8, `default not created` ); assert.equal( hv.debug, constants.DEFAULT_DEBUG, `default not created` ); assert.equal( hv.outputConstants, constants.DEFAULT_OUTPUTCONSTANTS, `default not created` ); assert.equal( hv.interval, constants.DEFAULT_INTERVAL, `default not created` ); assert.equal( hv.stateChangeResponseTime, constants.DEFAULT_STATE_CHANGE_RESPONSE_TIME, `default not created` ); assert.equal( hv.statusMsg, constants.DEFAULT_STATUSMSG, `default not created` ); assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `default not created` ); }); it( `HV can update variables`, ( done ) => { let data = { allowTLV8: false, debug: true, outputConstants: true, interval: 100, stateChangeResponseTime: 122, statusMsg: false, timeout: 141 }; let hv = new HV( ); // Simple check previously tested assert.equal( hv.allowTLV8, constants.DEFAULT_ALLOW_TLV8, `default not created` ); hv.update( data ); assert.equal( hv.allowTLV8, data.allowTLV8, `hv not updated` ); assert.equal( hv.debug, data.debug, `hv not updated` ); assert.equal( hv.outputConstants, data.outputConstants, `hv not updated` ); assert.equal( hv.interval, data.interval, `hv not updated` ); assert.equal( hv.stateChangeResponseTime, data.stateChangeResponseTime, `hv not updated` ); assert.equal( hv.statusMsg, data.statusMsg, `hv not updated` ); assert.equal( hv.timeout, data.timeout, `hv not updated` ); done( ); }); it( `Test HV can update updated variables`, ( done ) => { let data = { allowTLV8: false, debug: true, outputConstants: true, interval: 100, stateChangeResponseTime: 122, statusMsg: false, timeout: 141 }; let hv = new HV( ); hv.update( data ); let data2 = { allowTLV8: true, debug: false, outputConstants: false, interval: 300, // stateChangeResponseTime: 122, statusMsg: true, timeout: 333 }; hv.update( data2 ); assert.equal( hv.allowTLV8, data2.allowTLV8, `hv not updated` ); assert.equal( hv.debug, data2.debug, `hv not updated` ); assert.equal( hv.outputConstants, data2.outputConstants, `hv not updated` ); assert.equal( hv.interval, data2.interval, `hv not updated` ); assert.equal( hv.stateChangeResponseTime, data.stateChangeResponseTime, `hv not updated` ); assert.equal( hv.statusMsg, data2.statusMsg, `hv not updated` ); assert.equal( hv.timeout, data2.timeout, `hv not updated` ); done( ); }); it( `Test HV copy does not change original`, ( done ) => { const hv = new HV( ); let rHV = Object.assign( {}, hv ); assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `hv not initialized` ); rHV.timeout = 1500; assert.equal( rHV.timeout, 1500, `rhv not changed` ); assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `hv was changed` ); done( ); }); it( `Test HV copy does not change original`, ( done ) => { const hv = new HV( ); let rHV = Object.assign( {}, hv ); rHV.update = hv.update; assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `hv not initialized` ); let data = { allowTLV8: true, debug: false, outputConstants: false, interval: 300, // stateChangeResponseTime: 122, statusMsg: true, timeout: 333 }; rHV.update( data ); // Check changed assert.equal( rHV.allowTLV8, data.allowTLV8, `rHV not updated` ); assert.equal( rHV.debug, data.debug, `rHV not updated` ); assert.equal( rHV.outputConstants, data.outputConstants, `rHV not updated` ); assert.equal( rHV.interval, data.interval, `rHV not updated` ); assert.equal( rHV.stateChangeResponseTime, constants.DEFAULT_STATE_CHANGE_RESPONSE_TIME, `default not created` ); assert.equal( rHV.statusMsg, data.statusMsg, `rHV not updated` ); assert.equal( rHV.timeout, data.timeout, `rHV not updated` ); // Check original assert.equal( hv.allowTLV8, constants.DEFAULT_ALLOW_TLV8, `default was changed` ); assert.equal( hv.debug, constants.DEFAULT_DEBUG, `default was changed` ); assert.equal( hv.outputConstants, constants.DEFAULT_OUTPUTCONSTANTS, `default was changed` ); assert.equal( hv.interval, constants.DEFAULT_INTERVAL, `default was changed` ); assert.equal( hv.stateChangeResponseTime, constants.DEFAULT_STATE_CHANGE_RESPONSE_TIME, `default was changed` ); assert.equal( hv.statusMsg, constants.DEFAULT_STATUSMSG, `default was changed` ); assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `default was changed` ); done( ); }); }); ================================================ FILE: test/Logger.js ================================================ "use strict"; // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); // Let logger control logs for Unit Testing settings.cmd4Dbg = true; var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); let { Cmd4Accessory } = require( "../Cmd4Accessory" ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** TEST logger ************* describe('A simple logger Test', ( ) => { it( "Test bufferEnabled is off by default", ( ) => { const log = new Logger( ); log.setOutputEnabled( false ); let STDOUT_DATA="stdout_data"; log.info( STDOUT_DATA ); // Logger adds a \n so use include assert.equal( log.logBuf , "", `Expected no logs to stdout` ); assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf , "", `Expected no logs to stderr` ); assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` ); }); it( "Test info log to stdout gets captured and mothing else", ( ) => { const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let STDOUT_DATA="stdout_data"; log.info( STDOUT_DATA ); // Logger adds a \n so use include assert.include( log.logBuf , STDOUT_DATA, `Expected logs to stdout for log.info ` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf , "", `Expected no logs to stderr` ); assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` ); }); it( "Test warn log to stderr gets captured and mothing else", ( ) => { const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let STDERR_DATA="stderr_data"; log.warn( STDERR_DATA ); assert.equal( log.logBuf , "", `Expected no logs to stout` ); assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` ); // Logger adds a \n so use include assert.include( log.errBuf , STDERR_DATA, `Expected logs to stderr for log.warn ` ); assert.equal( log.errLineCount, 1 , `unexpected number of lines to stderr` ); }); it( "Test log to stderr gets captured and mothing else", ( ) => { const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let STDERR_DATA="stderr_data"; log.error( STDERR_DATA ); assert.equal( log.logBuf , "", `Expected no logs to stdout` ); assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` ); // Logger adds a \n so use include assert.include( log.errBuf , STDERR_DATA, `Expected logs to stderr` ); assert.equal( log.errLineCount, 1 , `unexpected number of lines to stderr` ); }); it( "Test log to stderr and different to stdout", ( ) => { const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let STDERR_DATA="stderr_data"; let STDOUT_DATA="stdout_data"; log.info( STDOUT_DATA ); log.error( STDERR_DATA ); // Logger adds a \n so use include assert.include( log.logBuf , STDOUT_DATA, `Expected logs to stdout for log.info ` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); // Logger adds a \n so use include assert.include( log.errBuf , STDERR_DATA, `Expected logs to stderr` ); assert.equal( log.errLineCount, 1 , `unexpected number of lines to stderr` ); }); it( "Test can create an instance of Cmd4Accessory with new logger", ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, state_cmd: "./test/echoScripts/echo_1" }; const log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let accessory = new Cmd4Accessory( log, config, _api, [ ] ); assert.instanceOf( accessory , Cmd4Accessory, "Expected accessory to be instance of Cmd4Accessory. Found %s" , accessory ); assert.equal( log.logBuf , "", `unexpected logs to stdout for a simple instance of Cmd4Accessory` ); assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf , "", `Expected no logs to stderr for a simple instance of Cmd4Accessory` ); assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` ); log.reset(); }); it( "Test can create an instance of Cmd4Accessory with a debug log", ( ) => { let config = { name: "Test Switch", type: "Switch", on: false, polling: true, state_cmd: "./test/echoScripts/echo_1" }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); new Cmd4Accessory( log, config, _api, [ ] ); assert.include( log.logBuf, "Creating Standalone Accessory type for ", `Expected debug logs to stdout with setDebugEnabled` ); //assert.equal( log.logLineCount, 17 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", `Expected no logs to stderr for a simple instance of Cmd4Accessory` ); assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` ); log.reset(); }); it( "Test logger performance of NOT enabled message", ( ) => { const log = new Logger( ); log.setBufferEnabled( false ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let entry = { characteristicString: "testCharacteristic", accessory: { displayName: "testDevice", queue: { inProgressGets: 0, inProgressSets: 0 } } }; let logStartTime = process.hrtime( ); log.debug( `OUTPUT FOR MEASUREMENT High priority "Get" queue interrupt attempted: ${ entry.accessory.displayName } ${ entry.characteristicString } inProgressSets:${ entry.accessory.queue.inProgressSets } inProgressGets: ${ entry.accessory.queue.inProgressGets }` ); let logTotalTime = process.hrtime( logStartTime ); let debug = false; let debugStartTime = process.hrtime( ); if ( debug ) log.debug( `OUTPUT FOR MEASUREMENT High priority "Get" queue interrupt attempted: ${ entry.accessory.displayName } ${ entry.characteristicString } inProgressSets:${ entry.accessory.queue.inProgressSets } inProgressGets: ${ entry.accessory.queue.inProgressGets }` ); let debugTotalTime = process.hrtime( debugStartTime ); let diff = logTotalTime[1] - debugTotalTime[1]; console.log( `logTotalTime: ${ logTotalTime[1] } debugTotalTime: ${ debugTotalTime[1] } diff: ${ diff }` ); assert.isAbove( logTotalTime[1], debugTotalTime[1], `Expected log total time to be greater than debug total time` ); }); }); ================================================ FILE: test/VariableTimer.js ================================================ "use strict"; let VariableTimer = require( "../utils/VariableTimer" ); describe('A Variable Timer Test', ( ) => { it( "Test creation of variableTimer", ( ) => { const timer = new VariableTimer( ); assert.instanceOf( timer , VariableTimer, "Expected timer to be instance of VariableTimer. Found %s" , timer ); assert.isFunction( timer.start, ".start is not a function" ); assert.isFunction( timer.stop, ".stop is not a function" ); assert.isFunction( timer.set_interval, ".set_interval is not a function" ); }); // Only skip this test because it takes 20 seconds it.skip( "Test timer can change intervals, stop start ...", ( done ) => { const timer = new VariableTimer( ); var start = new Date(); var total = 0; timer.start( ( ) => { var end = new Date(); var seconds = ( end - start) / 1000 total += Math.trunc( seconds ); start = end; console.log(`done ${ seconds } ${ total }`); }, 1000); assert.equal( timer.iv, 1000, "iv is set incorrectly" ); setTimeout(() => { console.log("changing timer to 2 seconds."); timer.set_interval( 2000 ); assert.equal( timer.iv, 2000, "iv is reset incorrectly" ); assert.equal( total, 4, "total is incorrect" ); setTimeout(() => { assert.equal( total, 12, "total is incorrect" ); timer.stop(); done(); }, 10000); }, 5000); }).timeout(20000); it( "Test timer can change intervals, only by .5s increments", ( done ) => { const timer = new VariableTimer( ); var start = new Date(); var total = 0; timer.start( ( ) => { var end = new Date(); var seconds = ( end - start) / 1000 total += Math.trunc( seconds ); start = end; console.log(`done ${ seconds } ${ total }`); }, 1000); assert.equal( timer.iv, 1000, "iv is set incorrectly" ); timer.set_interval( 2000 ); assert.equal( timer.iv, 2000, "iv reset to 2000 incorrectly" ); timer.set_interval( 2100 ); assert.equal( timer.iv, 2000, "iv reset incorrectly to 2100" ); timer.set_interval( 2500 ); assert.equal( timer.iv, 2500, "iv reset incorrectly to 2500" ); timer.set_interval( 2999 ); assert.equal( timer.iv, 2500, "iv reset incorrectly to 2999" ); timer.set_interval( 3000 ); assert.equal( timer.iv, 3000, "iv reset incorrectly to 3000" ); timer.set_interval( 2500 ); assert.equal( timer.iv, 2500, "iv reset incorrectly down to 2500" ); timer.stop(); done( ); }).timeout(20000); }); ================================================ FILE: test/allTests ================================================ test/Logger.js test/async-dump.js test/configHasCharacteristicProps.js test/isJSON.js test/indexOfEnum.js test/isNumeric.js test/extractKeyValue.js test/getAccessoryNameFunctions.js test/getAccessoryUUID.js test/getSetAllValues.js test/VariableTimer.js test/transposeCMD4Props.js test/trueTypeOf.js test/versionChecker.js test/Cmd4Storage.js test/HV.js test/cmd4Constants.js test/CMD4_CHAR_TYPE_ENUMS.js test/CMD4_ACC_TYPE_ENUM.js test/CMD4_DEVICE_TYPE_ENUM.js test/isAccDirective.js test/isDevDirective.js test/isCmd4Directive.js test/testOurConfig.json.js test/configTest.js test/loadPluginTest.js test/Cmd4Accessory.js test/internalRelatedTargetTests.js test/Cmd4Platform.js test/Cmd4AccessoryGetValue.js test/Cmd4AccessorySetValue.js test/Cmd4Mode.js test/Cmd4PriorityPollingQueue.js test/initPluginTest.js test/fakeGato.js test/pollingTest.js test/Cmd4PlatformRestartTests.js ================================================ FILE: test/async-dump.js ================================================ 'use strict'; // How To use: // // 1. In package.json change scipt "test", adding: // node_modules/.bin/mocha --require ./test/async-dump.js // 2. In the failing test add at the top: // after(function () { // global.asyncDump(); // }); // 3. Run the test // npm run test test/failingTest // // // Taken from: https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843 const { createHook } = require( 'async_hooks' ); const { stackTraceFilter } = require( 'mocha/lib/utils' ); const allResources = new Map(); // this will pull Mocha internals out of the stacks const filterStack = stackTraceFilter(); const hook = createHook({ init(asyncId, type, triggerAsyncId) { allResources.set(asyncId, { type, triggerAsyncId, stack: ( new Error( ) ).stack }); }, destroy( asyncId ) { allResources.delete( asyncId ); } }).enable( ); global.asyncDump = module.exports = ( ) => { hook.disable( ); console.error(` STUFF STILL IN THE EVENT LOOP:`) allResources.forEach(value => { console.error( `Type: ${value.type}` ); console.error( filterStack(value.stack ) ); console.error( '\n' ); }); }; ================================================ FILE: test/cmd4Constants.js ================================================ "use strict"; var fs = require("fs"); const constants = require( "../cmd4Constants" ); describe( "Testing cmd4Constants", function( ) { it( "All cmd4Constants should be declared the same.", function ( done ) { assert.equal( constants.STANDALONE, "Standalone", `Incorrect global value` ); assert.equal( constants.PLATFORM, "Platform", `Incorrect global value` ); assert.equal( constants.DEFAULT_TIMEOUT, 60000, `Incorrect global value` ); assert.equal( constants.DEFAULT_INTERVAL, 60000, `Incorrect global value` ); assert.equal( constants.DEFAULT_STATUSMSG, "TRUE", `Incorrect global value` ); assert.equal( constants.FAKEGATO_TYPE_ENERGY, "energy", `Incorrect global value` ); assert.equal( constants.FAKEGATO_TYPE_ROOM, "room", `Incorrect global value` ); assert.equal( constants.FAKEGATO_TYPE_WEATHER, "weather", `Incorrect global value` ); assert.equal( constants.FAKEGATO_TYPE_DOOR, "door", `Incorrect global value` ); assert.equal( constants.FAKEGATO_TYPE_MOTION, "motion", `Incorrect global value` ); assert.equal( constants.FAKEGATO_TYPE_THERMO, "thermo", `Incorrect global value` ); assert.equal( constants.FAKEGATO_TYPE_AQUA, "aqua", `Incorrect global value` ); assert.equal( constants.EVE, "eve", `Incorrect global value` ); assert.equal( constants.STORAGE, "storage", `Incorrect global value` ); assert.equal( constants.STORAGEPATH, "storagePath", `Incorrect global value` ); assert.equal( constants.FOLDER, "folder", `Incorrect global value` ); assert.equal( constants.KEYPATH, "keyPath", `Incorrect global value` ); assert.equal( constants.STATUS, "status", `Incorrect global value` ); assert.equal( constants.TEMP, "temp", `Incorrect global value` ); assert.equal( constants.SETTEMP, "setTemp", `Incorrect global value` ); assert.equal( constants.HUMIDITY, "humidity", `Incorrect global value` ); assert.equal( constants.PPM, "ppm", `Incorrect global value` ); assert.equal( constants.POWER, "power", `Incorrect global value` ); assert.equal( constants.PRESSURE, "pressure", `Incorrect global value` ); assert.equal( constants.CURRENTTEMP, "currentTemp", `Incorrect global value` ); assert.equal( constants.VALVEPOSITION, "valvePosition", `Incorrect global value` ); assert.equal( constants.WATERAMOUNT, "waterAmount", `Incorrect global value` ); assert.equal( constants.TIME, "time", `Incorrect global value` ); assert.equal( constants.PATH, "path", `Incorrect global value` ); assert.equal( constants.FS, "fs", `Incorrect global value` ); assert.equal( constants.GOOGLE_DRIVE, "googleDrive", `Incorrect global value` ); assert.equal( constants.OUTPUTCONSTANTS, "outputConstants", `Incorrect global value` ); assert.equal( constants.STATUSMSG, "statusMsg", `Incorrect global value` ); assert.equal( constants.TYPE, "type", `Incorrect global value` ); assert.equal( constants.SUBTYPE, "subType", `Incorrect global value` ); assert.equal( constants.DISPLAYNAME, "displayName", `Incorrect global value` ); assert.equal( constants.UUID, "uuid", `Incorrect global value` ); assert.equal( constants.ACCESSORY, "accessory", `Incorrect global value` ); assert.equal( constants.CATEGORY, "category", `Incorrect global value` ); assert.equal( constants.PUBLISHEXTERNALLY, "publishExternally", `Incorrect global value` ); assert.equal( constants.PROPS, "props", `Incorrect global value` ); assert.equal( constants.CHARACTERISTIC, "characteristic", `Incorrect global value` ); assert.equal( constants.TIMEOUT, "timeout", `Incorrect global value` ); assert.equal( constants.QUEUE, "queue", `Incorrect global value` ); assert.equal( constants.POLLING, "polling", `Incorrect global value` ); assert.equal( constants.INTERVAL, "interval", `Incorrect global value` ); assert.equal( constants.STATECHANGERESPONSETIME, "stateChangeResponseTime", `Incorrect global value` ); assert.equal( constants.STATE_CMD_PREFIX, "state_cmd_prefix", `Incorrect global value` ); assert.equal( constants.STATE_CMD_SUFFIX, "state_cmd_suffix", `Incorrect global value` ); assert.equal( constants.STATE_CMD, "state_cmd", `Incorrect global value` ); assert.equal( constants.FAKEGATO, "fakegato", `Incorrect global value` ); assert.equal( constants.REQUIRES, "requires", `Incorrect global value` ); assert.equal( constants.CONSTANTS, "constants", `Incorrect global value` ); assert.equal( constants.VARIABLES, "variables", `Incorrect global value` ); assert.equal( constants.LINKEDTYPES, "linkedTypes", `Incorrect global value` ); assert.equal( constants.ACCESSORIES, "accessories", `Incorrect global value` ); assert.equal( constants.URL, "url", `Incorrect global value` ); assert.equal( constants.ALLOWTLV8, "allowTLV8", `Incorrect global value` ); assert.equal( constants.CMD4_STORAGE_lv, "cmd4Storage", `Incorrect global value` ); done( ); }); }); describe( "Testing cmd4Constants ErrorStrings", function( ) { it( "All cmd4Constants error Strings should be declared correctly.", function ( done ) { let len = Object.keys( constants.ERROR_STRINGS ).length; assert.equal( constants.ERROR_STRING_MIN - constants.ERROR_STRING_MAX + 1, len, `Totals do not match` ); assert.equal( constants.ERROR_STRINGS[ constants.ERROR_STRING_MIN - constants.ERROR_TIMER_EXPIRED ], "Timer expired contacting accessory", "ERROR_TIMER_EXPIRED not defined correctly"); assert.equal( constants.ERROR_STRINGS[ constants.ERROR_STRING_MIN - constants.ERROR_NULL_REPLY ], "Reply is NULL", "ERROR_NULL_REPPLY not defined correctly"); done(); }); it( "cmd4Constants.errorString should be a function", ( ) => { assert.isFunction( constants.errorString, "errorString is not a function" ); }); it( "cmd4Constants errorString function works correctly.", function ( done ) { assert.equal( constants.errorString( constants.ERROR_TIMER_EXPIRED ), "Timer expired contacting accessory", "ERROR_TIMER_EXPIRED not defined correctly" ); assert.equal( constants.errorString( constants.ERROR_NO_DATA_REPLY ), "No data returned from accessory", "ERROR_NO_DATA_REPLY not defined correctly" ); done(); }); it( "cmd4Constants errorString limits.", function ( done ) { assert.equal( constants.errorString( constants.ERROR_STRING_MIN +1 ), "Device returned its own error; " + ( constants.ERROR_STRING_MIN +1 ), ".errorString MIN limit error" ); assert.equal( constants.errorString( constants.ERROR_STRING_MAX -1 ), "Device returned its own error; " + ( constants.ERROR_STRING_MAX -1 ), ".errorString MAX limit error" ); done(); }); }); describe( "Testing cmd4Constants - unused", function( ) { it( "All cmd4Constants should be used somewhere", function ( done ) { var cmd4Files = [ "./Cmd4Platform.js", "./Cmd4Accessory.js", "./Cmd4PriorityPollingQueue.js", "./utils/HV.js", "./index.js", "./lib/CMD4_DEVICE_TYPE_ENUM.js", "./tools/Cmd4AccDocGenerator", "./test/cmd4Constants.js", ]; // This is the count of the found constants in result let foundCount=0; let len = Object.keys( constants ).length; Object.keys( constants ).forEach( ( key ) => { // This one is not found yet let found = false; // This is the constant defind in cmd4Constants let c = key; // The regex to find the constant in the source files // A great regex site: // https://regex101.com/r/bE3c0x/5 const regex = new RegExp( `.*constants.${ c}.*$` ); // 115 //console.log("Looking for constants.%s ( %s of %s )", c, index, len ); // The constant must be in one of the Cmd4 source files for ( let fileIndex = 0; (fileIndex < cmd4Files.length ); fileIndex++ ) { let cmd4File = cmd4Files[ fileIndex ]; // Read in all the code from the source file let code = fs.readFileSync( cmd4File, "utf8" ); // If I could grep the source file I would, so // check the regex against each line var codeLines = code.split( '\n' ); let lineCount = 0; for ( let lineIndex = 0; lineIndex < codeLines.length; lineIndex++, lineCount++ ) { let line = codeLines[ lineIndex ]; // Check the regex let t = regex.test( line ); if ( t == true ) { found = true; foundCount++; break; } } if (found == true ) break; } if (found == false ) { console.log( "Not Found constants.%s", c ); assert.isTrue( found, `Not found constants.${ c } in source files` ); } }); console.log( "Total found was %s of %s ", foundCount, len ); assert.equal( foundCount, len , `Totals do not match` ); done( ); }).timeout(20000); }); describe( "Testing source constants - are defined", function( ) { it( "All Source constants should be defined", function ( done ) { var cmd4Files = [ "./Cmd4Platform.js", "./Cmd4Accessory.js", "./Cmd4PriorityPollingQueue.js", "./utils/HV.js", "./tools/Cmd4AccDocGenerator", "./lib/CMD4_DEVICE_TYPE_ENUM.js", "./tools/Cmd4AccDocGenerator" ]; let totalSourceConstants = 0; let foundCount = 0; // The constant must be in one of the Cmd4 source files for ( let fileIndex = 0; (fileIndex < cmd4Files.length ); fileIndex++ ) { let cmd4File = cmd4Files[ fileIndex ]; let sourceData = fs.readFileSync( cmd4File, "utf8" ); // Matches source files with stuff like: // constants.STANDALONE var sourceConstantsMatches = sourceData.toString().match(/constants.(\w).*\n/g); assert.isNotNull( sourceConstantsMatches, `Fike ${ cmd4File } has no constants and should be removed from this testcase` ); // This is the array of defined constants var sourceMappedConstantsArray = sourceConstantsMatches.map( ( s ) => { let returningArray = [ ]; let occurranceCountArray = s.match( /constants\./g ); if ( occurranceCountArray != null ) { for ( let i = 0; i < occurranceCountArray.length; i++ ) { // '{' - Used to cancel RegExp in vim s = s.slice( s.indexOf( "constants." ) + 10 ); const reg = new RegExp(`[\\s|,|:|;|}]|\\)|\\(` ); let sourceConstantLen = s.search( reg ); let sourceConstant = s.substr(0, sourceConstantLen ); if ( returningArray.length == 0 ) { //console.log("pushing -->%s<--", sourceConstant ); returningArray.push( sourceConstant ); } else if ( returningArray.find( ( entry ) => entry == sourceConstant ) == -1 ) { //console.log("pushing another -->%s<--", sourceConstant ); returningArray.push( sourceConstant ); } else { // console.log("Duplicate X->%s<-X", sourceConstant ); // Duplicate } // Proceed to the next constant within the same line if ( i + 1 < occurranceCountArray.length ) { s = s.slice( sourceConstant.length ) } } } // return [ "NAME", "NAME2" ... ] return returningArray; }); // Inside the mapping we created an array of constants because // there could be multiple constants per line. Therefore we need // to unwind the array of arrays. let sourceConstantsArray = [ ]; sourceMappedConstantsArray.forEach( ( item ) => { item.forEach( ( sourceConstant ) => { // console.log("Unwinding -->%s<--", sourceConstant ); sourceConstantsArray.push( sourceConstant ); }); }); totalSourceConstants += sourceConstantsArray.length; // Over every source constant, find if defined in cmd4Constants for ( let sIndex = 0; sIndex < sourceConstantsArray.length; sIndex++) { let sourceConstant = sourceConstantsArray[ sIndex ]; //if ( constants.indexOf( sourceConstant ) == -1 ) if ( constants[ sourceConstant ] == undefined ) { console.log( "Not Found from: %s -->%s<--", cmd4File, sourceConstant ); assert( "Not Found from: %s -->%s<--", cmd4File, sourceConstant ); } else { //console.log( "FOUND -->%s<--", sourceConstant ); foundCount ++ } } } console.log( "Total found was %s of %s", foundCount, totalSourceConstants ); // If you are off by 1 or 2, check that the code does not // have something like constants.QUEUETYPE_WORM2) // Where the bracket follow the constant without a space assert.equal( foundCount, totalSourceConstants, `Totals do not match` ); done( ); }).timeout(20000); }); ================================================ FILE: test/configHasCharacteristicProps.js ================================================ "use strict"; // ***************** TEST LOADING ********************** let { Cmd4Accessory } = require( "../Cmd4Accessory" ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); let changedMinStep = 1; // Was .1 let changedMinValue = 16; // Was -27 let changedMaxValue = 32; // Was 100 // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // *** TEST configHasCharacteristicProps ******* describe('Test configHasCharacteristicProps.', ( ) => { it('configHasCharacteristicProps should be a function', ( ) => { let config = { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.isFunction(accessory.configHasCharacteristicProps, "configHasCharacteristicProps is not a function" ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyTemperatureSensor", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it('configHasCharaceristicProps should work with full props.', ( ) => { let config = { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; let props = { TargetTemperature: { maxValue: +100, minValue: -100, minStep: 0.1, } }; let result = accessory.configHasCharacteristicProps(accTypeEnumIndex, props, CMD4_ACC_TYPE_ENUM ); assert.isObject(CMD4_ACC_TYPE_ENUM, "configHasCharacteristicProps of valid data with full properties returned incorrect result: " + result ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyTemperatureSensor", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it('configHasCharaceristicProps should work with property in small Caps.', ( ) => { let config = { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; let props = { TargetTemperature: { maxValue: +100, minValue: -100, minStep: 0.1, } }; let result = accessory.configHasCharacteristicProps(accTypeEnumIndex, props, CMD4_ACC_TYPE_ENUM ); assert.isObject(CMD4_ACC_TYPE_ENUM, "configHasCharacteristicProps of valid data in small Caps returned incorrect result: " + result ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyTemperatureSensor", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it('configHasCharaceristicProps should work with two properties', ( ) => { let config = { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; let props = { TargetTemperature: { maxValue: +100, minValue: -100, minStep: 0.1, } }; let result = accessory.configHasCharacteristicProps(accTypeEnumIndex, props, CMD4_ACC_TYPE_ENUM ); assert.isObject(CMD4_ACC_TYPE_ENUM, "configHasCharacteristicProps of valid data in small Caps returned incorrect result: " + result ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyTemperatureSensor", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it('configHasCharaceristicProps should work with one property.', ( ) => { let config = { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; accessory.props = { TargetTemperature: { maxValue: +100 } }; let result = accessory.configHasCharacteristicProps( accTypeEnumIndex ); assert.isObject(result, "configHasCharacteristicProps of valid data with one propertyreturned incorrect result: " + result ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyTemperatureSensor", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it('configHasCharaceristicProps should work with two characteristics.', ( ) => { let config = { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; accessory.props = { CurrentTemperature: { maxValue: +100 }, TargetTemperature: { maxValue: +100 }, }; let result = accessory.configHasCharacteristicProps( accTypeEnumIndex ); assert.isObject(result, "configHasCharacteristicProps of valid data with two characteristicsreturned incorrect result: " + result ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyTemperatureSensor", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it('configHasCharaceristicProps should fail with a unknown prop', ( ) => { let config = { type: "TemperatureSensor", displayName: "MyTemperatureSensor", name: "MyTemperatureSensor", currentTemperature: 25, statusFault: "NO_FAULT", }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TargetTemperature; accessory.props = { TargetTemperature: { maxValue: +100, minValue: -100, uhoh: 0.1 } }; expect ( ( ) => accessory.configHasCharacteristicProps( accTypeEnumIndex ) ).to.throw(/props for key "uhoh" not in definition of "targetTemperature"/); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyTemperatureSensor", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` ); assert.equal( log.errBuf, "", ` Cmd4Accessory Unxexpected stderr: ${ log.errBuf }` ); }); it('setProps should be called for two defind characteristics', ( ) => { let config = { type: "Thermostat", displayName: "MyThermostat", name: "MyThermostat", currentTemperature: 25, targetTemperature: 25, statusFault: "NO_FAULT", props: { "currentTemperature": { "maxValue": 32, "minValue": 16, "minStep": 1 }, "targetTemperature": { "maxValue": 32, "minValue": 16, "minStep": 1 } } }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.isFunction(accessory.configHasCharacteristicProps, "configHasCharacteristicProps is not a function" ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyThermostat", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, "[90mOverriding characteristic CurrentTemperature props for: MyThermostat", `Unxexpected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, "[90mOverriding characteristic TargetTemperature props for: MyThermostat", `Unxexpected stdout: ${ log.logBuf }` ); }); it('setProps should alter characteristics', ( ) => { let config = { type: "Thermostat", displayName: "MyThermostat", name: "MyThermostat", currentTemperature: 25, targetTemperature: 25, statusFault: "NO_FAULT", props: { "currentTemperature": { "maxValue": 32, "minValue": 16, "minStep": 1 }, "targetTemperature": { "maxValue": 32, "minValue": 16, "minStep": 1 } } }; let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let parentInfo = undefined; let accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.include( log.logBuf, "[34mCmd4 is running in Demo Mode for MyThermostat", ` Cmd4Accessory Unxexpected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, "[90mOverriding characteristic CurrentTemperature props for: MyThermostat", `Unxexpected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, "[90mOverriding characteristic TargetTemperature props for: MyThermostat", `Unxexpected stdout: ${ log.logBuf }` ); let props = accessory.service.getCharacteristic( CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.CurrentTemperature ].characteristic).props; //let util = require('util'); //console.log(util.inspect(props, {depth: null})); // test minValue let hapMinValue = props.minValue; // Was -27 now 16 let hapMaxValue = props.maxValue; // Was 100 now 32 let hapMinStep = props.minStep; // Was .1 now 1 assert.equal( hapMinValue, changedMinValue, `minValue: ${ hapMinValue } not equal to expected: ${ changedMinValue }` ); assert.equal( hapMaxValue, changedMaxValue, `maxValue: ${ hapMaxValue } not equal to expected: ${ changedMaxValue }` ); assert.equal( hapMinStep, changedMinStep, `minStep: ${ hapMinStep } not equal to expected: ${ changedMinStep }` ); }); }); ================================================ FILE: test/configTest.js ================================================ 'use strict'; var _api = new HomebridgeAPI( ); // object we feed to Plugins let indexOfEnum = require( "../utils/indexOfEnum" ); Object.defineProperty( exports, "indexOfEnum", { enumerable: true, get: function ( ) { return indexOfEnum.indexOfEnum; } }); const cmd4Config = require( "../Extras/config.json" ); const isJSON = require( "../utils/isJSON" ); const ucFirst = require( "../utils/ucFirst" ); const lcFirst = require( "../utils/lcFirst" ); // Constants const constants = require( "../cmd4Constants" ); describe( `Testing our config.json`, ( ) => { it( `cmdConfig should be a JSON object`, ( ) => { assert.isObject( cmd4Config, `cmd4Config is not an object` ); }); }) // This would be the plugin un-initialized var pluginModule = require( "../index" ); describe( `Initializing our plugin module`, ( ) => {}); var cmd4 = pluginModule.default( _api ); let CMD4_ACC_TYPE_ENUM = cmd4.CMD4_ACC_TYPE_ENUM; let CMD4_DEVICE_TYPE_ENUM = cmd4.CMD4_DEVICE_TYPE_ENUM; describe( `Testing our config.json`, ( ) => { const accessories = cmd4Config.platforms[ 0 ].accessories; for ( let index=0; index < accessories.length; index ++ ) { testAccessoryConfig( accessories[ index ] ); } }); function testAccessoryConfig ( accessoryConfig ) { describe( `Testing Device Name: ${ accessoryConfig.displayName }`, ( ) => { for ( let key in accessoryConfig ) { let value = accessoryConfig[ key ]; //let ucKey = ucFirst( key ); let lcKey = lcFirst( key ); switch ( lcKey ) { case constants.TYPE: testType ( value ); break; case constants.DISPLAYNAME: testName( value ); break; case constants.UUID: testName( value ); break; case constants.ACCESSORY: break; case constants.CATEGORY: break; case constants.PUBLISHEXTERNALLY: break; case constants.PROPS: break; case constants.NAME: testName( value ); break; case constants.MODEL: testModel( value ); break; case constants.MANUFACTURER: testName( value ); break; case constants.SERIALNUMBER: testName( value ); break; case constants.FIRMWAREREVISION: break; case constants.OUTPUTCONSTANTS: break; case constants.TIMEOUT: testTimeout( value ); break; case constants.POLLING: testPollingConfig( value ); break; case constants.CMD4_MODE: testCmd4_Mode( value ); break; case constants.INTERVAL: testInterval( value ); break; case constants.STATECHANGERESPONSETIME: testStateChangeResponseTime( value ); break; case constants.STATE_CMD_PREFIX: break; case constants.STATE_CMD_SUFFIX: break; case constants.STATE_CMD: testStateCmd( value ); break; case constants.STORAGE: case constants.STORAGEPATH: case constants.FOLDER: case constants.KEYPATH: break; case constants.FAKEGATO: testFakegatoConfig( value ); break; case constants.REQUIRES: break; case constants.CONSTANTS: processConstantsConfig( value ); break; case constants.VARIABLES: processVariablesConfig( value ); break; case constants.LINKEDTYPES: processLinkedTypesConfig ( value ); break; case constants.ACCESSORIES: break; case constants.URL: break; default: { //console.log( "1 %s Testing %s", accessoryConfig.displayName, value); testCharacteristic( key, value ); } } } // Test that we define displayName it( `Testing for a displayName`, ( ) => { assert.isNotNull( accessoryConfig.displayName , `displayName cannot be undefined` ); }); }); } function testConstantKey( key ) { it( `Constant key: ${ key } must start with "\${"`, ( ) => { assert.isTrue( key!=null && key.startsWith( '${' ), `Constant: "${ key }" does not start with "\${"` ); }); it( `Constant key: ${ key } should end with "}"`, ( ) => { assert.isTrue( key!=null && key.endsWith( "}" ), `Constant: "${ key }" does not end with "}"` ); }); } function processConstantsConfig( config ) { if ( Array.isArray( config ) ) { for ( let i = 0; i < config.length; i++ ) { processConstantsConfig( config[ i ] ); } return; } if ( isJSON( config ) ) { describe( "test JSON Config", ( ) => {}); // I assume only 1, but you know about assuming ... for ( let key in config ) { testConstantKey( key ); } return; } it( `Constant Config must be valid`, ( ) => { assert.isTrue( isJSON( config ), `Constants: "${ config }" must be an array of/or list of key/value pairs` ); }); } function testVariableKey( key ) { it( `Variable key: "${ key }" must start with "\${"`, ( ) => { assert.isTrue( key!=null && key.startsWith( '${' ), `Variable: "${ key }" does not start with "\${"` ); }); it( `Variable key: "${ key }" should end with "}"`, ( ) => { assert.isTrue( key!=null && key.endsWith( '}' ), `Variable "${ key }" does not end with "}"` ); }); } function processVariablesConfig( config ) { if ( Array.isArray( config )) { for ( let i = 0; i < config.length; i++ ) { processVariablesConfig( config[ i ] ); } return; } if ( isJSON ( config ) ) { // I assume only 1, but you know about assuming ... for ( let key in config ) { let valueToAdd = config[ key ] ; testVariableKey( key ); // remove any leading and trailing single quotes // so that using it for replacement will be easier. valueToAdd.replace( /^'/, "" ) valueToAdd.replace( /'$/, "" ) // Need to test is constants exist for valueToAdd return; } } it( "Variable Config must be valid", ( ) => { assert.isTrue( isJSON( config ), `Variable: "${ config }" must be an array of/or list of key/value pairs` ); }); } function processLinkedTypesConfig( config ) { if ( Array.isArray ( config ) ) { for ( let i = 0; i < config.length; i++ ) { processLinkedTypesConfig( config[ i ] ); } return; } if ( isJSON ( config ) ) { describe( `Processing Linked accessory: ${ config.displayName }`, ( ) => {}); testAccessoryConfig( config ); return; } it( `Variable Config must be valid`, ( ) => { assert.isTrue( isJSON( config ), `LinkedTypes: "${ config }" must be an array of/or list of key/value pairs` ); }); } function testType( type ) { let ucKeyIndex = CMD4_DEVICE_TYPE_ENUM.properties.indexOfEnum( i => i.deviceName === type); it( `Device Type: "${ type }" should be valid`, ( ) => { assert.isAbove( ucKeyIndex, -1, `Invalid device type: ${ type }` ); }); } function testName( name ) { it( `Device name: ${ name } should be valid`, ( ) => { assert.isString( name, `Invalid name: ${ name }` ); }); } function testModel ( model ) { it( `Device model: ${ model } should be valid`, ( ) => { assert.isString( model, `Invalid model: ${ model }` ); }); } function testStateChangeResponseTime ( stateChangeResponseTime ) { it( `Device stateChangeResponseTime: ${ stateChangeResponseTime } should be valid`, ( ) => { assert.isNumber( stateChangeResponseTime, `Invalid stateChangeResponseTime: ${ stateChangeResponseTime }` ); }); } function testInterval( interval ) { it( `Device interval: ${ interval } should be valid`, ( ) => { assert.isNumber( interval, `Invalid interval: ${ interval }` ); }); } function testCmd4_Mode( cmd4Mode ) { it( `Cmd4_Mode: ${ cmd4Mode } should be a valid string`, ( ) => { let ucCmd4Mode = ucFirst( cmd4Mode ); assert.equal( ucCmd4Mode, "Demo", `Invalid Cmd4_Mode: ${ cmd4Mode }` ); }); } function testTimeout( timeout ) { it( `Device timeout: ${ timeout } should be a valid number`, ( ) => { assert.isNumber( timeout, "Invalid timeout:" + timeout ); }); } function testStateCmd ( state_cmd ) { it( `Device state_cmd: ${ state_cmd } should be valid`, ( ) => { assert.isString( state_cmd, `Invalid state_cmd: ${ state_cmd }` ); }); } function testCharacteristicString ( characteristic ) { describe( `Testing characteristic string: ${ characteristic }`, ( ) => { let ucCharacteristic = ucFirst( characteristic ); let characteristicIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === ucCharacteristic ); it( `Characteristic ${ characteristic } should be valid`, ( ) => { assert.isAbove( characteristicIndex, -1, `Invalid characteristic: ${ characteristic }` ); }); }); } function testCharacteristic ( characteristic, value ) { describe( `Testing characteristic: ${characteristic }`, ( ) => { let characteristicIndex = CMD4_ACC_TYPE_ENUM.indexOfEnum( characteristic ); it( `Characteristic index: ${ characteristicIndex } should be valid`, ( ) => { assert.isAbove( characteristicIndex, -1, `Invalid characteristic index` ); }); it( `Characteristic ${ characteristic } should be valid`, ( ) => { assert.isNumber( characteristicIndex, `Invalid characteristic : ${ characteristic }` ); }); // Check if properties is not null it( `Characteristic ${ characteristic } properties should be valid`, ( ) => { assert.isNotNull( CMD4_ACC_TYPE_ENUM.properties, `properties is null: ${ characteristic }` ); }); // Check if properties[ charisticIndex ] is not null it( `Characteristic ${ characteristic } properties[ ${ characteristicIndex } should be valid`, ( ) => { assert.isNotNull( CMD4_ACC_TYPE_ENUM.properties[ characteristicIndex ], `properties is null: ${ characteristic }` ); }); // Check if the characteristic has constant values to test if ( Object.keys( CMD4_ACC_TYPE_ENUM.properties[ characteristicIndex ].validValues).length > 0 ) { it( `Value: "${ value }" should be valid characteristic value`, ( ) => { assert.property( CMD4_ACC_TYPE_ENUM.properties[ characteristicIndex ].validValues, value, `Could not find " ${ value }" for characteristic: ${ characteristic }` ); }); } }); } function testPollingConfig( pollingConfig ) { switch ( typeof pollingConfig ) { case "object": { it( `Device polling: ${ pollingConfig } should be an array`, ( ) => { assert.isArray( pollingConfig, "Invalid polling:" + pollingConfig ); }); for ( let cindex=0; cindex < pollingConfig.length; cindex ++) { describe( `Testing polling object:`, ( ) => { for ( let pollingKey in pollingConfig[ cindex ] ) { switch ( pollingKey) { case constants.TIMEOUT: { testTimeout ( pollingConfig[ cindex ].timeout ); break; } case constants.INTERVAL: testInterval( pollingConfig[ cindex ].interval ); break; case constants.CHARACTERISTIC: { let value = pollingConfig[ cindex ].characteristic; // console.log( "2 Testing %s", value); testCharacteristicString( value ); break; } default: { let value = pollingConfig[ cindex ][ pollingKey ]; // console.log( "3 Testing %s", value); describe( `Testing polling characteristic: ${ pollingKey }`, ( ) => { testCharacteristic ( pollingKey, value ); }); } } } }); } break; } case "boolean": { it( `Device polling: ${ pollingConfig } should be a boolean`, ( ) => { assert.isBoolean( pollingConfig, `Invalid polling: ${ pollingConfig }` ); }); break; } default: { it( `Device polling: ${ pollingConfig } should be an array or string`, ( ) => { assert.typeOf( typeof pollingConfig, "number", `Invalid characteristic polling type: ${ pollingConfig }` ); }); } } } function testFakegatoConfig( fakegatoConfig ) { switch ( typeof fakegatoConfig ) { case "object": { // FakeGato Object - OK for ( let fakegatoKey in fakegatoConfig ) { let ucFakegatoKey = ucFirst( fakegatoKey ); switch ( ucFakegatoKey ) { case constants.EVE: case constants.FOLDER: case constants.KEYPATH: case constants.STORAGE: case constants.STORAGEPATH: case constants.POLLING: { // console.log( "Found ucKey '%s'", ucFakegatoKey ); break; } default: { //console.log( "Found default ucKey '%s'", ucFakegatoKey ); } } } break; } default: { it( `fakegato: ${ typeof fakegato } should be an object`, ( ) => { assert.typeOf( typeof fakegatoConfig, "number", `Invalid fakegato type: ${ typeof fakegatoConfig }` ); }); } } } ================================================ FILE: test/echoScripts/echo_0 ================================================ #!/bin/bash stdbuf -o0 echo 0; exit 0; ================================================ FILE: test/echoScripts/echo_1 ================================================ #!/bin/bash stdbuf -o0 echo "1"; exit 0; ================================================ FILE: test/echoScripts/echo_ACTIVE ================================================ #!/bin/bash echo "blast" > /tmp/blast; stdbuf -o0 echo ACTIVE; exit 0; ================================================ FILE: test/echoScripts/echo_DISABLED ================================================ #!/bin/bash stdbuf -o0 echo "DISABLED"; exit 0; ================================================ FILE: test/echoScripts/echo_ENABLED ================================================ #!/bin/bash stdbuf -o0 echo "Enabled"; exit 0; ================================================ FILE: test/echoScripts/echo_INACTIVE ================================================ #!/bin/bash stdbuf -o0 echo INACTIVE; exit 0; ================================================ FILE: test/echoScripts/echo_On ================================================ #!/bin/bash stdbuf -o0 echo On; exit 0; ================================================ FILE: test/echoScripts/echo_after5seconds ================================================ #!/bin/bash sleep 5; stdbuf -o0 echo false; exit 0; ================================================ FILE: test/echoScripts/echo_errorToStderr ================================================ #!/bin/bash stdbuf -e0 -i0 echo "This message goes to stderr" >&2; exit 0; ================================================ FILE: test/echoScripts/echo_false ================================================ #!/bin/bash stdbuf -o0 echo false; exit 0; ================================================ FILE: test/echoScripts/echo_nothing ================================================ #!/bin/bash stdbuf -o0 echo ""; exit 0; ================================================ FILE: test/echoScripts/echo_null ================================================ #!/bin/bash stdbuf -o0 echo "null"; exit 0; ================================================ FILE: test/echoScripts/echo_nullAndErrorToStderr ================================================ #!/bin/bash stdbuf -o0 echo null stdbuf -o0 e0 echo "This message goes to stderr" >&2; exit 0; ================================================ FILE: test/echoScripts/echo_quoted0 ================================================ #!/bin/bash stdbuf -o0 echo \"0\"; exit 0; ================================================ FILE: test/echoScripts/echo_quoted1 ================================================ #!/bin/bash stdbuf -o0 echo \"1\"; exit 0; ================================================ FILE: test/echoScripts/echo_quotedFALSE ================================================ #!/bin/bash stdbuf -o0 echo \"False\"; exit 0; ================================================ FILE: test/echoScripts/echo_quotedNULL ================================================ #!/bin/bash stdbuf -o0 echo \"NULL\"; exit 0; ================================================ FILE: test/echoScripts/echo_quotedNothing ================================================ #!/bin/bash stdbuf -o0 echo \" \"; exit 0; ================================================ FILE: test/echoScripts/echo_quotedTRUE ================================================ #!/bin/bash stdbuf -o0 echo \"True\"; exit 0; ================================================ FILE: test/echoScripts/echo_too_much ================================================ #!/bin/bash stdbuf -o0 echo ""; stdbuf -o0 echo "second extra line"; stdbuf -o0 echo "third extra line"; exit 0; ================================================ FILE: test/echoScripts/echo_true ================================================ #!/bin/bash stdbuf -o0 echo true; exit 0; ================================================ FILE: test/echoScripts/echo_true_withRcOf1 ================================================ #!/bin/bash stdbuf -o0 echo true; exit 1; ================================================ FILE: test/echoScripts/justExitWithRCof0 ================================================ #!/bin/bash exit 0; ================================================ FILE: test/echoScripts/justExitWithRCof1 ================================================ #!/bin/bash exit 1; ================================================ FILE: test/echoScripts/runToTimeoutRcOf0 ================================================ #!/bin/bash sleep 1000000; exit 0; ================================================ FILE: test/echoScripts/runToTimeoutRcOf1 ================================================ #!/bin/bash sleep 1000000; exit 1; ================================================ FILE: test/echoScripts/testGetSetValues.js ================================================ #!/opt/homebrew/bin/node const fs = require( "fs" ); function showHelp() { console.log(` Syntax: Get Device < fn > Set Device [< value >] < fn > Set writes stuff to < fn > as a required fn with INPUTS.IO INPUTS.DEVICE INPUTS.CHARACTERISTIC INPUTS.VALUE Get echos what Set would have sent ` ); process.exit( 666 ); } function createDeviceFileName( fn, DEVICE, CHARACTERISTIC ) { return `${ fn }_${ DEVICE }_${ CHARACTERISTIC }`; } function doGet( ) { if ( ELEMENTS != 6 ) { showHelp( ); process.exit( 1 ); } let fn = process.argv[5]; let deviceFile = createDeviceFileName( fn, DEVICE, CHARACTERISTIC ); let INPUTS; try { INPUTS = require( deviceFile ); } catch ( e ) { console.log(`Cannot load fn: ${ deviceFile }, error: ${ e }`); process.exit( 666 ); } console.log( `${ INPUTS.VALUE }` ); process.exit( 0 ); } function doSet( ) { if ( ELEMENTS != 7 ) { showHelp( ); process.exit( 1 ); }; let VALUE = process.argv[5]; let fn = process.argv[6]; let deviceFile = createDeviceFileName( fn, DEVICE, CHARACTERISTIC ); // Skip the fn ELEMENTS--; // Remove the old fn try { fs.unlinkSync( deviceFile ); } catch(err) { // Don't care // console.error( err ) // process.exit( 666 ); } let data = `exports.IO="${ IO }"; exports.DEVICE="${ DEVICE }"; exports.CHARACTERISTIC="${ CHARACTERISTIC }"; exports.VALUE="${ VALUE }";\n`; // Write the arguments as requires fs.writeFileSync( deviceFile, data, function( err ) { console.log(` Error writing to fn: ${ deviceFile } error: ${ err }\n`); process.exit( 666 ); }); process.exit( 0 ); } let ELEMENTS = process.argv.length; if ( ELEMENTS < 4 ) { showHelp( ); process.exit( 1 ); } let IO = process.argv[2]; let DEVICE = process.argv[3]; let CHARACTERISTIC = process.argv[4]; switch( IO ) { case "Get": doGet( ); break; case "Set": doSet( ); break; default: console.log(`Invalid Get/Set: ${IO}` ); showHelp(); } process.exit( 1 ); ================================================ FILE: test/extractKeyValue.js ================================================ 'use strict'; var CMD4_ACC_TYPE_ENUM = { AccessoryFlags: 0, properties: {} }; CMD4_ACC_TYPE_ENUM.properties = { 0: { type: "AccessoryFlags", // characteristic: Characteristic.AccessoryFlags, // props: {format: Characteristic.Formats.UINT32, // perms: [Characteristic.Perms.READ, // Characteristic.Perms.NOTIFY // ] // }, validValues: {"OPEN": 0, "CLOSED": 1, "OPENING": 2, "CLOSING": 3, "STOPPED": 4 } } }; var extractKeyValue = require( "../utils/extractKeyValue.js" ); describe( "Testing extractKeyValue", ( ) => { it( "extractKeyValue should be a function", ( ) => { assert.isFunction( extractKeyValue, "extractKeyValue is not a function" ); }); it( "test 1 extractKeyValue should return correct key", ( ) => { let expectedKey = "CLOSING"; let value = 3; let result = extractKeyValue( CMD4_ACC_TYPE_ENUM.properties[0].validValues, value ); assert.equal( result, expectedKey, "Test 1 extractKeyValue( " + value + " ) returned:" + result + " expected:" + expectedKey ); }); it( "test 2 extractKeyValue should return undefined for no value", ( ) => { let expectedKey = undefined; let value = undefined; let result = extractKeyValue( CMD4_ACC_TYPE_ENUM.properties["0"].validValues, value ); assert.equal( result, expectedKey, "Test 1 extractKeyValue( " + value + " ) returned:" + result + " expected:" + expectedKey ); }); }); ================================================ FILE: test/fakeGato.js ================================================ #!node // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); let Cmd4Accessory = require( "../Cmd4Accessory" ).Cmd4Accessory; let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; let Logger = require( "../utils/Logger" ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // Taken from https://stackoverflow.com/questions/11731072/dividing-an-array-by-filter-function //function partition(array, predicate) //{ // return array.reduce( ( acc, item ) => ( acc[+!predicate( item )].push( item ), acc ), [ [], [] ] ); //} // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( `Quick Test of CMD4_DEVICE_TYPE_ENUM`, ( ) => { it( `CMD4_DEVICE_TYPE_ENUM.EOL ${ DEVICE_EOL }`, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( `Quick Test of CMD4_ACC_TYPE_ENUM`, ( ) => { it( `CMD4_ACC_TYPE_ENUM.EOL = ${ ACC_EOL }`, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); describe('Testing FakeGato gets created', ( ) => { before( ( ) => { }); after( ( ) => { }); beforeEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach(function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; // MaxListenersExceededWarning: Possible EventEmitter memory leak detected _api.removeAllListeners(); }); it( `Test if Cmd4Platform exists`, function ( ) { expect( Cmd4Platform ).not.to.be.a( "null", `Cmd4Platform was null` ); }); it( `Test creation of Accessory with FakeGato Thermo`, function( done ) { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "On" }, { characteristic: "Brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits:"CELSIUS", active: "INACTIVE", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, stateChangeResponseTime: 1, polling: [ { characteristic: "CurrentTemperature" }, { characteristic: "TargetTemperature" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice", fakegato: { eve: "thermo", currentTemp: "CurrentTemperature", setTemp: "TargetTemperature", valvePosition: 0, storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" } } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, `cmd4Platform is not an instance of Cmd4Platform` ); assert.equal( "", log.logBuf, ` Cmd4Platform unexpected stdout received: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` Cmd4Platform unexpected stderr received: ${ log.errBuf }` ); cmd4Platform.discoverDevices( ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4ThermostatAccessory = cmd4Platform.createdCmd4Accessories[1]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Light is not an instance of Cmd4Accessory` ); expect( cmd4ThermostatAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Thermostat is not an instance of Cmd4Accessory` ); done( ); }); it( `Test creation of Accessory with FakeGato room`, function( done ) { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "On" }, { characteristic: "Brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { type: "Thermostat", name: "Thermostat", displayName: "Thermostat", temperatureDisplayUnits: "CELSIUS", active: "INACTIVE", currentTemperature: 20.0, targetTemperature: 20.0, currentHeatingCoolingState: 0, targetHeatingCoolingState: 0, currentRelativeHumidity: 44, stateChangeResponseTime: 1, polling: [ { characteristic: "CurrentTemperature" }, { characteristic: "TargetTemperature" }, { characteristic: "CurrentRelativeHumidity" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice", fakegato: { eve: "room", temp: "CurrentTemperature", humidity: "CurrentRelativeHumidity", storage: "fs", storagePath: ".homebridge/FakegatoStorage" } } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, `cmd4Platform is not an instance of Cmd4Platform` ); assert.equal( "", log.logBuf, ` Cmd4Platform unexpected stdout received: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` Cmd4Platform unexpected stderr received: ${ log.errBuf }` ); cmd4Platform.discoverDevices( ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4ThermostatAccessory = cmd4Platform.createdCmd4Accessories[1]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Light is not an instance of Cmd4Accessory` ); expect( cmd4ThermostatAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Thermostat is not an instance of Cmd4Accessory` ); done( ); }); it( `Test creation of Accessory with FakeGato door`, function( done ) { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "On" }, { characteristic: "Brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { type: "Door", name: "MyDoor", currentPosition: 0, targetPosition: 0, contactSensorState: "CONTACT_DETECTED", holdPosition: "FALSE", obstructionDetected: "FALSE", statusActive: 0, statusFault: 0, polling: [ { characteristic: "StatusActive" }, { characteristic: "StatusFault" } ], fakegato: { eve: "door", status: "StatusActive", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, stateChangeResponseTime: 3, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, `cmd4Platform is not an instance of Cmd4Platform` ); assert.equal( "", log.logBuf, ` Cmd4Platform unexpected stdout received: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` Cmd4Platform unexpected stderr received: ${ log.errBuf }` ); cmd4Platform.discoverDevices( ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4DoorAccessory = cmd4Platform.createdCmd4Accessories[1]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Light is not an instance of Cmd4Accessory` ); expect( cmd4DoorAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Door is not an instance of Cmd4Accessory` ); done( ); }); it( `Test creation of Accessory with FakeGato power`, function( done ) { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "On" }, { characteristic: "Brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { type: "Fan", name: "Myv1Fan", rotationDirection: 1, rotationSpeed: 100, stateChangeResponseTime: 1, polling: [ { characteristic: "RotationDirection" }, { characteristic: "RotationSpeed" }, { characteristic: "On" } ], fakegato: { eve: "energy", // rotationSpeed used as an example. // Not really power characteristic power: "RotationSpeed", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, `cmd4Platform is not an instance of Cmd4Platform` ); assert.equal( "", log.logBuf, ` Cmd4Platform unexpected stdout received: ${ log.logBuf }` ); assert.equal( "", log.errBuf, ` Cmd4Platform unexpected stderr received: ${ log.errBuf }` ); cmd4Platform.discoverDevices( ); let cmd4LightAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4FanAccessory = cmd4Platform.createdCmd4Accessories[1]; expect( cmd4LightAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Light is not an instance of Cmd4Accessory` ); expect( cmd4FanAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Fan is not an instance of Cmd4Accessory` ); done( ); }); it( `Test incorrect characteristic throws fakegato error `, function( done ) { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "On" }, { characteristic: "Brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { type: "Fan", name: "Myv1Fan", rotationDirection: 1, rotationSpeed: 100, stateChangeResponseTime: 1, polling: [ { characteristic: "RotationDirection" }, { characteristic: "On" } ], fakegato: { eve: "energy", // An invalid characteristic. power: "BadData", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect ( ( ) => cmd4Platform.discoverDevices( ) ).to.throw(/Invalid characteristic "BadData" for fakegato to log of "power"./); done( ); }); it( `Test not defining polling characteristic throws fakegato error `, function( done ) { let platformConfig = { accessories: [ { name: "MyLight", displayName: "MyLight", statusMsg: true, type: "Lightbulb", on: 0, brightness: 100, polling: [ { characteristic: "On" }, { characteristic: "Brightness" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }, { type: "Fan", name: "Myv1Fan", rotationDirection: 1, rotationSpeed: 100, stateChangeResponseTime: 1, polling: [ { characteristic: "RotationDirection" }, { characteristic: "On" } ], fakegato: { eve: "energy", // rotationSpeed used as an example. // Not really power characteristic power: "rotationSpeed", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect ( ( ) => cmd4Platform.discoverDevices( ) ).to.throw(/Characteristic: "rotationSpeed" for fakegato to log of "power" is not being polled.\nHistory can not be updated continiously./); done( ); }); it( `Test invalid Eve type throws error `, function( done ) { let platformConfig = { accessories: [ { type: "Fan", name: "Myv1Fan", rotationDirection: 1, rotationSpeed: 100, stateChangeResponseTime: 1, polling: [ { characteristic: "RotationDirection" }, { characteristic: "RotationSpeed" }, { characteristic: "On" } ], fakegato: { eve: "BadType", // rotationSpeed used as an example. // Not really power characteristic power: "RotationSpeed", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect ( ( ) => cmd4Platform.discoverDevices( ) ).to.throw(/Invalid fakegato eve type: "BadType". It must be one of \( energy, room, weather, door, motion, thermo, aqua \). Check the Cmd4 README at: "https:\/\/github.com\/simont77\/fakegato-history"/); done( ); }); it( `Test invalid fakegato key throws error `, function( done ) { let platformConfig = { accessories: [ { type: "Fan", name: "Myv1Fan", rotationDirection: 1, rotationSpeed: 100, stateChangeResponseTime: 1, polling: [ { characteristic: "RotationDirection" }, { characteristic: "RotationSpeed" }, { characteristic: "On" } ], fakegato: { eve: "energy", badKey: "badValue", // rotationSpeed used as an example. // Not really power characteristic power: "RotationSpeed", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect ( ( ) => cmd4Platform.discoverDevices( ) ).to.throw(/Invalid fakegato key: "badKey" in json.config for: "Myv1Fan"./); done( ); }); it( `Test uppercase FakeGato Key throws an error. It Always did `, function( done ) { let platformConfig = { accessories: [ { type: "Fan", name: "Myv1Fan", rotationDirection: 1, rotationSpeed: 100, stateChangeResponseTime: 1, polling: [ { characteristic: "rotationDirection" }, { characteristic: "rotationSpeed" }, { characteristic: "on" } ], fakegato: { eve: "Energy", // rotationSpeed used as an example. // Not really power characteristic power: "rotationSpeed", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect ( ( ) => cmd4Platform.discoverDevices( ) ).to.throw(/Invalid fakegato eve type: "Energy". It must be one of \( energy, room, weather, door, motion, thermo, aqua \). Check the Cmd4 README at: "https:\/\/github.com\/simont77\/fakegato-history"./); assert.equal( 2, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); done( ); }); it( `Test uppercase FakeGato Characteristic creates a warning `, function( done ) { let platformConfig = { accessories: [ { type: "Fan", name: "Myv1Fan", rotationDirection: 1, rotationSpeed: 100, stateChangeResponseTime: 1, polling: [ { characteristic: "rotationDirection" }, { characteristic: "rotationSpeed" }, { characteristic: "on" } ], fakegato: { eve: "energy", // rotationSpeed used as an example. // Not really power characteristic power: "RotationSpeed", storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); let cmd4FanAccessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4FanAccessory ).to.be.a.instanceOf( Cmd4Accessory, `Fan is not an instance of Cmd4Accessory` ); //assert.equal( "", log.logBuf, ` Cmd4Platform unexpected stdout received: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mThe config.json FakeGato characteristic: RotationSpeed is Capitalized it should be: rotationSpeed. In the near future this will be an Error so that Cmd4 can use homebridge-ui`, ` Cmd4Platform unexpected stderr received: ${ log.errBuf }` ); assert.equal( 3, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); done( ); }); it( `Test FakeGato updateAccessoryAttribute creates a log for THERMO`, function( done ) { let platformConfig = { accessories: [ { type: "Thermostat", name: "MyThermostat", displayName: "MyThermostat", currentHeatingCoolingState: "OFF", targetHeatingCoolingState: "OFF", currentTemperature: 22.2, targetTemperature: 22.2, temperatureDisplayUnits: "CELSIUS", currentRelativeHumidity: 60, targetRelativeHumidity: 60, coolingThresholdTemperature: 21.4, heatingThresholdTemperature: 20.2, polling: [ { characteristic: "currentTemperature" }, { characteristic: "targetTemperature" } ], fakegato: { eve: "thermo", currentTemp: "currentTemperature", setTemp: "targetTemperature", valvePosition: 0, storage: "fs", storagePath: ".homebridge/FakegatoStorage", folder: "folderName", keyPath: "/place/to/store/my/keys/" }, state_cmd: "./test/echoScripts/echo_ACTIVE" } ] } let log = new Logger( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); cmd4Platform.discoverDevices( ); let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0]; expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, `Fan is not an instance of Cmd4Accessory` ); assert.include( log.logBuf, `[39m Adding new platformAccessory: MyThermostat` ); assert.include( log.logBuf, `35mConfiguring platformAccessory: \u001b[39mMyThermostat` ); assert.equal( 2, log.logLineCount, ` Unexpected number of stdout lines: ${ log.logBuf }` ); //assert.equal( "", log.logBuf, ` Unexpected stdout: ${ log.logBuf }` ); assert.equal( 0, log.errLineCount, ` setCachedValue logged lines than one: ${ log.errBuf }` ); log.reset( ); log.setBufferEnabled( true ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); cmd4Accessory.updateAccessoryAttribute( CMD4_ACC_TYPE_ENUM.CurrentTemperature, 18); assert.include( log.logBuf, `[39m \u001b[90mLogging currentTemp: 18 setTemp:22.2 valvePosition:0`, `Incorrect stdout: ${ log.logBuf }` ); done( ); }); }); ================================================ FILE: test/getAccessoryNameFunctions.js ================================================ 'use strict'; const { getAccessoryName, getAccessoryDisplayName } = require( "../utils/getAccessoryNameFunctions" ); describe( "Testing getAccessoryName LC", ( ) => { it( "getAccessoryName.js should be a function", ( ) => { assert.isFunction( getAccessoryName, "getAccessoryName is not a function" ); }); it( "getAccessoryName should choose name first LC ", ( ) => { let config = {displayName: "Kodi", name: "blah" }; let expectedResult = "blah"; let result = getAccessoryName( config ); assert.equal( result, expectedResult, "getAccessoryName should return name over displayName. result: " + result + " expected: " + expectedResult ); }); it( "getAccessoryName should choose displayName second LC ", ( ) => { let config = { noName: "blah", displayName: "Kodi" }; let expectedResult = "Kodi"; let result = getAccessoryName( config ); assert.equal( result, expectedResult, "getAccessoryName should return displayName over name. result: " + result + " expected: " + expectedResult ); }); }) describe( "Testing getAccessoryDisplayName LC", ( ) => { it( "getAccessoryDisplayName.js should be a function LC", ( ) => { assert.isFunction( getAccessoryDisplayName, "getAccessoryDisplayName is not a function" ); }); it( "getAccessoryDisplayName should choose displayName first LC ", ( ) => { let config = {displayName: "Kodi", name: "blah" }; let expectedResult = "Kodi"; let result = getAccessoryDisplayName( config ); assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult ); }); it( "getAccessoryDisplayName should choose name second LC ", ( ) => { let config = { name: "Kodi", configuredName: "blah" }; let expectedResult = "Kodi"; let result = getAccessoryDisplayName( config ); assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult ); }); }) describe( "Testing getAccessoryName UC", ( ) => { it( "getAccessoryName should choose name first UC ", ( ) => { let config = {displayName: "Kodi", name: "blah" }; let expectedResult = "blah"; let result = getAccessoryName( config ); assert.equal( result, expectedResult, "getAccessoryName should return name over displayName. result: " + result + " expected: " + expectedResult ); }); it( "getAccessoryName should choose displayName second UC ", ( ) => { let config = { NoName: "blah", displayName: "Kodi" }; let expectedResult = "Kodi"; let result = getAccessoryName( config ); assert.equal( result, expectedResult, "getAccessoryName should return displayName over name. result: " + result + " expected: " + expectedResult ); }); }) describe( "Testing getAccessoryDisplayName.js", ( ) => { it( "getAccessoryDisplayName.js should be a function UC", ( ) => { assert.isFunction( getAccessoryDisplayName, "getAccessoryDisplayName is not a function" ); }); it( "getAccessoryDisplayName should choose displayName first UC ", ( ) => { let config = {displayName: "Kodi", name: "blah" }; let expectedResult = "Kodi"; let result = getAccessoryDisplayName( config ); assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult ); }); it( "getAccessoryDisplayName should choose name second UC ", ( ) => { let config = { name: "Kodi", configuredName: "blah" }; let expectedResult = "Kodi"; let result = getAccessoryDisplayName( config ); assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult ); }); }) /* Mucking about. Result, just use getAccessoryName describe( "Testing logic of config.name", ( ) => { it.skip( "name should equal whatever config.[Nn]ame is", ( ) => { let config = { Name: "Np", ConfiguredName: "Np" }; let configPassedToGetAccessoryNane = { Name: "Kodi", ConfiguredName: "blah" }; // This does not do what I thought let name = config.name = config.name = config.Name || getAccessoryName( configPassedToGetAccessoryNane ); let expectedResult = "Kodi"; // This fails with: // AssertionError: getAccessoryDisplayName should return displayName over name. result: Np expected: Kodi: expected 'Np' to equal 'Kodi' // + expected - actual // -Np // +Kodi assert.equal( name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult ); assert.equal( config.name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult ); assert.equal( config.Name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult ); }); it.skip( "name should equal whatever config.[Nn]ame is", ( ) => { let config = { Name: "Np", ConfiguredName: "Np" }; let configPassedToGetAccessoryNane = { Name: "Kodi", ConfiguredName: "blah" }; // This does do what is expected, but config.Name never gets set. let name = config.name = config.name || getAccessoryName( configPassedToGetAccessoryNane ); let expectedResult = "Kodi"; assert.equal( name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult ); assert.equal( config.name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult ); // This fails with: // AssertionError: getAccessoryDisplayName should return displayName over name. result: Kodi expected: Kodi: expected 'Np' to equal 'Kodi' // + expected - actual // -Np // +Kodi assert.equal( config.Name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult ); }); }) */ ================================================ FILE: test/getAccessoryUUID.js ================================================ "use strict"; var _api = new HomebridgeAPI(); // object we feed to Plugins var pluginModule = require( "../index" ); var getAccessoryUUID = require( "../utils/getAccessoryUUID.js" ); describe("Quick Testing load of index.js", ( ) => { it("API should not be null", ( ) => { assert.isNotNull(_api, "_api is null" ); }); it("index.js loaded should not be null", ( ) => { assert.isNotNull(pluginModule, "loading resulted in null" ); }); it("UUIDGen should be found", ( ) => { var t = typeof _api.hap.uuid.generate; assert.equal(t, "function" ); }); }); describe( "Testing getAccessoryUUID.js", ( ) => { it( "getAccessoryUUID.js should be a function", ( ) => { assert.isFunction( getAccessoryUUID, "getAccessoryUUID is not a function" ); }); it( "getAccessoryUUID should return a string ", ( ) => { let config = {displayName: "Kodi", name: "blah" }; let result = getAccessoryUUID( config, _api.hap.uuid); assert.isString( result, "getAccessoryUUID should return a string. result:: " + result ); }); it( "getAccessoryUUID should return a string length 36 ", ( ) => { let config = { name: "Kodi", configuredUUID: "blah" }; let result = getAccessoryUUID( config, _api.hap.uuid); assert.isString( result, "getAccessoryUUID should return a string. result:: " + result ); assert.equal( result.length, 36, "getAccessoryUUID return a string length of 36. result:: " + result.length ); }); }) ================================================ FILE: test/getSetAllValues.js ================================================ 'use strict'; const os = require( "os" ); const cmd4StateDir = os.homedir( ) + "/.homebridge/Cmd4Scripts/Cmd4States/" var glob = require( "glob" ); describe( 'Cleaning up any old Cmd4States/Status_Device_* files ...', ( ) => { glob( cmd4StateDir + "Status_Device_*", null, function ( er, files ) { for ( var file of files ) { // To use the promise of unlink, it must be in an async function // so put it in one. Why not unLinkSync, because for whatever reason // some files were notbremoved synchronously. ( async( ) => { await fs.unlink( file, function( err, result ) { it('file:' + file +' should be removed', function ( done ) { if ( err && err.code != 'ENOENT' ) assert.isNull( err, 'file not removed err: ' + err + " result: " + result ); done( ); }); }); }); } }) }); function removeStateFileThatShouldAlreadyBeRemoved( characteristicString ) { let stateFile = cmd4StateDir + "Status_Device_" + characteristicString; if (fs.existsSync( stateFile )) { // console.log(' *** BLAST! The path exists.' + stateFile ); // To use the promise of unlink, it must be in an async function // so put it in one. Why not unLinkSync, because for whatever reason // some files were notbremoved synchronously. ( async( ) => { fs.unlink( stateFile, function( err, result ) { if ( err && err.code != 'ENOENT' ) assert.isNull( err, 'file not removed err: ' + err + " result: " + result ); }); }); } } // ***************** TEST LOADING ********************** // This would be the plugin un-initialized var pluginModule = require( "../index" ); describe( "Testing load of index.js", ( ) => { it( "index.js loaded should not be null", ( ) => { assert.isNotNull( pluginModule, "loading resulted in null" ); }); var t = typeof pluginModule.default; it( "index.js default initializer should be found", ( ) => { assert.equal( t, "function"); }); }); // ************ TEST UNINITIALIZED CMD4_DEVICE_TYPE_ENUM EOL ************** describe( "Quick Testing CMD4_DEVICE_TYPE_ENUM EOL", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM has EOL", ( ) => { assert.isNotNull( pluginModule.DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM.EOL, "EOL is null" ); }); it( "CMD4_DEVICE_TYPE_ENUM.EOL = " + DEVICE_EOL, ( ) => { assert.equal( pluginModule.DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL ); }); }); var _api = new HomebridgeAPI( ); // object we feed to Plugins describe( "Testing homebridge API", ( ) => { it( "API should not be null", ( ) => { assert.isNotNull( _api, "_api is null"); }); }); describe( "Initializing our plugin module", ( ) => {}); var cmd4 = pluginModule.default( _api ); // ***************** TEST State.js ********************** const child_process = require( "child_process" ); // *** TEST Get of Characteristic properties of default written data ******* describe( "Testing State.js Get Characteristics default written data ( " + CMD4_ACC_TYPE_ENUM.EOL + " of them )", ( ) => { for ( let accTypeEnumIndex=0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex ++ ) { let characteristicString = cmd4.CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].type; let cmd = "./Extras/Cmd4Scripts/State.js"; let args2 = " Get Device " + characteristicString + " "; let args = ["Get", "Device", characteristicString ]; it( accTypeEnumIndex + ": " + cmd + args2 + "should return something", ( ) => { // Why twice with two awaits, beats me ! removeStateFileThatShouldAlreadyBeRemoved( characteristicString ); const ps = child_process.spawnSync( cmd, args ); var data="not set by me"; if ( ps.status !== 0 ) { assert.equal( ps.status, 0, "Process error stdout: " + ps.stdout + " stderr: " + ps.stderr + " status: " + ps.status + " signal: " + ps.signal ); } else { data = ps.stdout; assert( data != "", "data returned '" + data + "'" ); assert( data.length != 0, "data returned: " + data.length ); } }); } }); // *** TEST Set of Characteristic properties ******* let dummyData = "X0X0Test"; let dummyDataR = "\"" + dummyData + "\""; describe( `Testing State.js Set Characteristics ( ${ CMD4_ACC_TYPE_ENUM.EOL } of them )`, ( ) => { for ( let accTypeEnumIndex=0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex ++ ) { let characteristicString = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].type; let cmd = "./Extras/Cmd4Scripts/State.js"; let args2 = " Set Device " + characteristicString + " " + dummyData; let args = ["Set", "Device", characteristicString, dummyData]; const ps = child_process.spawnSync( cmd, args ); it( accTypeEnumIndex + ": " + cmd + args2 + " should return no data", ( ) => { var data="not set by me"; // console.log( "status is '%s'", ps.status ); if ( ps.status !== 0 ) { assert.equal( ps.status, 0, "Process error stdout: " + ps.stdout + " stderr: " + ps.stderr + " status: " + ps.status + " signal: " + ps.signal ); } else { data = ps.stdout; assert( data == "", "No data should be returned: '" + data + "'" ); } }); } }); // *** TEST Get of Characteristic properties after set of dummy data ******* describe( "Begin Testing", ( ) => {}); // *** TEST Get of Characteristic properties of written data ******* describe( "Testing State.js Get Characteristics written data ( " + cmd4.CMD4_ACC_TYPE_ENUM.EOL + " of them ). Note: May fail even number of times ????", ( ) => { for ( let accTypeEnumIndex=0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex ++ ) { let characteristicString = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].type; let cmd = "./Extras/Cmd4Scripts/State.js"; let args2 = " Get Device " + characteristicString + " "; let args = ["Get", "Device", characteristicString]; it( accTypeEnumIndex + ": " + cmd + args2 + "should return: " + dummyDataR , ( ) => { const ps = child_process.spawnSync( cmd, args ); var data="not set by me"; if ( ps.status !== 0 ) { assert.equal( ps.status, 0, "Process error stdout: " + ps.stdout + " stderr: " + ps.stderr + " status: " + ps.status + " signal: " + ps.signal ); } else { data = ps.stdout; // fixme ??? assert( data == dummyData, "data returned: '" + data + "'" ); // The carriage return is stripped by getValue assert( data == dummyDataR, "data returned:" + data ); assert( data.length != 0, "data returned: " + data.length ); } }); } }); ================================================ FILE: test/indexOfEnum.js ================================================ 'use strict'; let { indexOfEnum } = require( "../utils/indexOfEnum" ); Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } }); // ***************** TEST Plugin Initialized Variables *************** describe( "Initializing our plugin module", ( ) => {}); let _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** TEST indexOfEnum .************* describe( "Test indexOfEnum import", ( ) => { it( "indexOfEnum should be a function ", ( ) => { assert.isFunction( indexOfEnum, "index of enum is not a function. Found: %s", typeof indexOfEnum ); }); }); describe( "Test indexOfEnum", ( ) => { it( "indexOfEnum should be identify a deviceName ", ( ) => { let j = CMD4_DEVICE_TYPE_ENUM.WiFiSatellite; let name = CMD4_DEVICE_TYPE_ENUM.properties[ j ].deviceName; let ucKeyIndex = CMD4_DEVICE_TYPE_ENUM.properties.indexOfEnum( i => i.deviceName === name ); assert.equal( j, ucKeyIndex, "index of enum should identify the device %s(%s). Found: %s", name, j, ucKeyIndex ); }); it( "indexOfEnum should identify a Characteristic Name ", ( ) => { let j = CMD4_ACC_TYPE_ENUM.BatteryLevel; let type = CMD4_ACC_TYPE_ENUM.properties[ j ].type; let ucKeyIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === type); assert.equal( j, ucKeyIndex, "index of enum should identify the characteristic " + type + "(" + j + "). Found: " + ucKeyIndex ); }); it( "indexOfEnum should identify a Characteristic Name ", ( ) => { let j = CMD4_ACC_TYPE_ENUM.CurrentRelativeHumidity; //let j = CMD4_ACC_TYPE_ENUM.CurrentHumidifierDehumidifierState; //let j = CMD4_ACC_TYPE_ENUM.blast; console.log("j=" + j); let type = CMD4_ACC_TYPE_ENUM.properties[ j ].type; let ucKeyIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === type); assert.equal( j, ucKeyIndex, "index of enum should identify the characteristic " + type + "(" + j + "). Found: " + ucKeyIndex ); }); describe( "Test each characteristic type", ( ) => { for ( let index = 0; index < ACC_EOL; index++ ) { it( "indexOfEnum should be identify a Characteristic Type ", ( ) => { let type = CMD4_ACC_TYPE_ENUM.properties[ index ].type; let ucKeyIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === type); assert.equal( index, ucKeyIndex, "index of enum should identify the characteristic " + type + "(" + index + "). Found: " + ucKeyIndex ); }); } }); }); ================================================ FILE: test/initPluginTest.js ================================================ "use strict"; // ***************** TEST LOADING ********************** var pluginModule = require( "../index" ); describe(`Testing load of index.js`, ( ) => { it( `index.js loaded should not be null`, ( ) => { assert.isNotNull(pluginModule, `loading resulted in null` ); }); var t = typeof pluginModule.default; it( `index.js default initializer should be found`, ( ) => { assert.equal(t, `function` ); }); }); describe( `Testing homebridge API`, ( ) => { it( `API should not be null`, ( ) => { let apiInstance = new HomebridgeAPI(); assert.isNotNull( apiInstance, ` apiInstance is null` ); }); }); describe( `Testing homebridge setup`, ( ) => { it( `HAP Categories should not be null`, ( ) => { let apiInstance = new HomebridgeAPI(); assert.isNotNull( apiInstance.hap.Categories, `Categories is null` ); }); it( `HAP Characteristic should be a function`, ( ) => { let apiInstance = new HomebridgeAPI(); assert.isFunction( apiInstance.hap.Characteristic, "Characteristic is not an function" ); }); it( `HAP Accessory should be a function`, ( ) => { let apiInstance = new HomebridgeAPI(); assert.isFunction( apiInstance.hap.Accessory, `apiInstance.hap.Accessory is not an function` ); }); it( `HAP Service should be a function`, ( ) => { let apiInstance = new HomebridgeAPI(); assert.isFunction( apiInstance.hap.Service, ` apiInstance.hap.Service is not an function` ); }); }); // ***************** TEST Plugin Un Initialized Variables *************** describe( `Testing index.js plugin unInitialized variables.`, ( ) => { it( `Plugin CMD4_DEVICE_TYPE_ENUM should be a object`, ( ) => { assert.isObject(CMD4_DEVICE_TYPE_ENUM, `CMD4_DEVICE_TYPE_ENUM is not an object` ); }); it( `Plugin CMD4_ACC_TYPE_ENUM should be a object`, ( ) => { assert.isObject(CMD4_ACC_TYPE_ENUM, "CMD4_ACC_TYPE_ENUM is not an object" ); }); it( `Plugin CMD4_ACC_TYPE_ENUM.EOL should be defined`, ( ) => { assert.equal(CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL, `CMD4_DEVICE_TYPE_ENUM.EOL is incorrect` ); }); it( `Plugin CMD4_ACC_TYPE_ENUM.EOL should be defined`, ( ) => { assert.equal(CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, "CMD4_ACC_TYPE_ENUM.EOL is incorrect" ); }); }); // ***************** TEST Plugin Initialized Variables *************** describe( `Testing index.js plugin Initialized variables.`, ( ) => { it( `Initialized Plugin CMD4_DEVICE_TYPE_ENUM.EOL should be correct`, ( ) => { let apiInstance = new HomebridgeAPI(); let cmd4 = pluginModule.default( apiInstance ); assert.equal(cmd4.CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL, "returned CMD4_DEVICE_TYPE_ENUM.EOL is incorrect" ); }); it( `Initialized Plugin returned CMD4_ACC_TYPE_ENUM.EOL should be correct`, ( ) => { let apiInstance = new HomebridgeAPI(); let cmd4 = pluginModule.default( apiInstance ); assert.equal(cmd4.CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, `returned CMD4_ACC_TYPE_ENUM.EOL is incorrect` ); }); it( `Initialized Plugin returned CMD4_DEVICE_TYPE_ENUM.properties length should be correct`, ( ) => { let apiInstance = new HomebridgeAPI(); let cmd4 = pluginModule.default( apiInstance ); let properties = cmd4.CMD4_DEVICE_TYPE_ENUM.properties; assert.equal(Object.keys(properties).length, DEVICE_EOL, 'returned CMD4_DEVICE_TYPE_ENUM.properties length is incorrect' ); }); it( `Initialized Plugin returned CMD4_ACC_TYPE_ENUM.properties length should be correct`, ( ) => { let apiInstance = new HomebridgeAPI(); let cmd4 = pluginModule.default( apiInstance ); let properties = cmd4.CMD4_ACC_TYPE_ENUM.properties; assert.equal(Object.keys(properties).length, ACC_EOL, 'returned CMD4_ACC_TYPE_ENUM.properties length is incorrect' ); }); it( `Initialized Plugin returned CMD4_ACC_TYPE_ENUM.properties[0-${ ACC_EOL }].Characteristic should be defined`, ( ) => { let apiInstance = new HomebridgeAPI(); let cmd4 = pluginModule.default( apiInstance ); let properties = cmd4.CMD4_ACC_TYPE_ENUM.properties; for ( let accTypeEnumIndex = 0; accTypeEnumIndex < ACC_EOL; accTypeEnumIndex++ ) { assert.isNotNull( properties[ accTypeEnumIndex ].characteristic, `Characteristic at index: ${ accTypeEnumIndex } is null. found ${ properties[ accTypeEnumIndex ].characteristic }` ); } }); it( `Initialized Plugin returned CMD4_DEVICE_TYPE_ENUM.properties[0-${ DEVICE_EOL }].service should be defined`, ( ) => { let apiInstance = new HomebridgeAPI(); let cmd4 = pluginModule.default( apiInstance ); let properties = cmd4.CMD4_DEVICE_TYPE_ENUM.properties; for ( let deviceTypeEnumIndex = 0; deviceTypeEnumIndex < DEVICE_EOL; deviceTypeEnumIndex++ ) { if ( properties[ deviceTypeEnumIndex ].deprecated == true ) continue; assert.isNotNull( properties[ deviceTypeEnumIndex ].service, `service at index: ${ deviceTypeEnumIndex } is null.. found ${ properties[ deviceTypeEnumIndex ].service }` ); } }); /* // While this works individualy, it intercepts loading from other unit tests it( `Initialized Plugin returned CMD4_DEVICE_TYPE_ENUM.properties[0-${ DEVICE_EOL }].service should be defined`, ( ) => { let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; var apiInstance = new HomebridgeAPI(); // object we feed to Plugins let cmd4 = pluginModule.default( apiInstance ); let CMD4_DEVICE_TYPE_ENUM = cmd4.CMD4_DEVICE_TYPE_ENUM; let CMD4_ACC_TYPE_ENUM = cmd4.CMD4_ACC_TYPE_ENUM; assert.equal( CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL, "returned CMD4_DEVICE_TYPE_ENUM.EOL is incorrect" ); assert.equal( CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, `returned CMD4_ACC_TYPE_ENUM.EOL is incorrect` ); let config = { "platform": "Cmd4", "name": "Cmd4", "accessories": [ { "type": "Switch", "name": "PS_4", "displayName": "PS_4", "on": false, } ] } let cmd4Platform = new Cmd4Platform( null, config, apiInstance ); let log = cmd4Platform.log; log.setBufferEnabled( ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); assert.equal( log.logBuf, "", ` cmd4Platform unexpected output received: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` cmd4Platform unexpected error output received: ${ log.errBuf }` ); log.reset( ); apiInstance.emit("didFinishLaunching"); assert.include( log.logBuf, `Cmd4Platform didFinishLaunching`, `didFinishLaunching Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "", ` cmd4Platform Unexpected stderr: ${ log.errBuf }` ); }); */ }); ================================================ FILE: test/internalRelatedTargetTests.js ================================================ "use strict"; // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); var Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; describe( "Initializing our CMD4 Libraries ", ( ) => {}); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); describe('Testing isRelatedTargetCharacteristicInSameDevice', ( ) => { afterEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; }); it('isRelatedTargetCharacteristicInSameDevice returns correctly for TemperatureSensor with no REQUIRED *Target* Characteristic', ( ) => { let platformConfig = { accessories: [ { statusMsg: true, type: "TemperatureSensor", displayName: "TemperatureSensor", name: "TemperatureSensor", currentTemperature: "22.2", polling: [ { characteristic: "currentTemperature" }], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : TemperatureSensor`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCharacteristic polling for: TemperatureSensor`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: TemperatureSensor`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[33mAdding getCachedValue for TemperatureSensor characteristic: Name`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mSetting up accessory: TemperatureSensor for polling of: CurrentTemperature timeout: 60000 interval: 60000 queueName: "Q:TemperatureSensor"`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[33mAdding priorityGetValue for TemperatureSensor characteristic: CurrentTemperature`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mSetting up accessory: TemperatureSensor for polling of: CurrentTemperature timeout: 60000 interval: 60000`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); // Not Target setValue assert.notInclude( log.logBuf, `[33mAdding prioritySetValue for TemperatureSensor characteristic: TargetTemperature`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); // Not Target polling assert.notInclude( log.logBuf, `[90mSetting up accessory: TemperatureSensor for polling of: TargetTemperature timeout: 60000 interval: 60000`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); // No errors assert.equal( log.errBuf, "", ` Cmd4Accessory Unexpected stderr: ${ log.errBuf }` ); }); it('isRelatedTargetCharacteristicInSameDevice returns correctly for TemperatureSensor with no REQUIRED *Target* Characteristic With QUEUE', ( done ) => { let platformConfig = { queueTypes: [{ queue: "A", queueType: "WoRm" }], accessories: [ { statusMsg: true, type: "TemperatureSensor", displayName: "TemperatureSensor", name: "TemperatureSensor", queue: "A", currentTemperature: "22.2", polling: [ { characteristic: "currentTemperature" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : TemperatureSensor`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCharacteristic polling for: TemperatureSensor`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: TemperatureSensor`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[33mAdding getCachedValue for TemperatureSensor characteristic: Name`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mSetting up accessory: TemperatureSensor for polling of: CurrentTemperature timeout: 60000 interval: 60000 queueName: "A"`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[33mAdding priorityGetValue for TemperatureSensor characteristic: CurrentTemperature`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mSetting up accessory: TemperatureSensor for polling of: CurrentTemperature timeout: 60000 interval: 60000 queueName: "A"`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); // Not setValue assert.notInclude( log.logBuf, `[33mAdding prioritySetValue for TemperatureSensor characteristic: TargetTemperature`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); // Not polling assert.notInclude( log.logBuf, `[90mSetting up accessory: TemperatureSensor for polling of: TargetTemperature timeout: 60000 interval: 60000 queueName: "A"`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); // No errors assert.equal( log.errBuf, "", ` Cmd4Accessory Unexpected stderr: ${ log.errBuf }` ); done( ); }); it('Polling complains related polling characteristic is missing', ( done ) => { let platformConfig = { accessories: [ { name: "Door", displayName: "Door", statusMsg: true, type: "Door", currentPosition: 0, targetPosition: 0, positionState: 0, polling: [ { characteristic: "currentPosition" }, // { characteristic: "TargetPosition" }, { characteristic: "positionState" } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.include( log.logBuf, `[34mCreating Platform Accessory type for : Door`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCharacteristic polling for: Door`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mCreated platformAccessory: Door`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[33mAdding getCachedValue for Door characteristic: Name`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[33mAdding priorityGetValue for Door characteristic: CurrentPosition`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mWarning, With polling for "currentPosition" requested, you also must do polling of "targetPosition" or things will not function properl` , `Incorrect stderr: ${ log.errBuf }` ); assert.notInclude( log.logBuf, `[33mAdding prioritySetValue for Door characteristic: TargetPosition`, ` Cmd4Accessory Incorrect stdout: ${ log.logBuf }` ); done( ); }); }); ================================================ FILE: test/isAccDirective.js ================================================ 'use strict'; let { indexOfEnum } = require( "../utils/indexOfEnum" ); Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } }); let isAccDirective = require( "../utils/isAccDirective" ); var _api = new HomebridgeAPI(); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); // ******** TEST isAccDirectivw .************* describe( `Test isAccDirective import`, ( ) => { it( `isAccDirectivw should be a function `, ( ) => { assert.isFunction( isAccDirective, `isAccDirective is not a function. Found: ${ typeof isAccDirective }` ); }); }); describe( `Test isAccDirective`, ( ) => { it( `isAccDirective should identify a type `, ( ) => { let type = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let rc = isAccDirective( type ); assert.isString( rc.type, `Unexpected result for isAccDirective` ); assert.equal( rc.type, type, `Expected result to be "on"` ); assert.equal( rc.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `UnExpected result` ); }); it( `isAccDirective should NOT identify an uppercase type `, ( ) => { let ucType = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); let rc = isAccDirective( ucType ); assert.isNull( rc.accTypeEnumIndex, `Expected result to be null` ); }); it( `isAccDirective should NOT identify an unknown type `, ( ) => { let type = "Blast"; let rc = isAccDirective( type ); assert.isNull( rc.accTypeEnumIndex, `Expected result to be null` ); }); it( `isAccDirective should identify an uppercase type if upperCase is checked`, ( ) => { let type = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On ); let ucType = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On ); let rc = isAccDirective( ucType, true ); assert.isString( rc.type, `Unexpected result for isAccDirective` ); assert.equal( rc.type, type, `Expected result to be "on"` ); assert.equal( rc.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `UnExpected result` ); }); }); ================================================ FILE: test/isCmd4Directive.js ================================================ 'use strict'; let { indexOfEnum } = require( "../utils/indexOfEnum" ); Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } }); let isCmd4Directive = require( "../utils/isCmd4Directive" ); var _api = new HomebridgeAPI(); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); // ******** QUICK TEST of SETUP ************* describe('Quick Test of Setup', ( ) => { it( `CMD4_ACC_TYPE_ENUM.EOL = ${ ACC_EOL }`, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** TEST isCmd4Directive .************* describe( `Test isCmd4Directive import`, ( ) => { it( `isCmd4Directive should be a function `, ( ) => { assert.isFunction( isCmd4Directive, `isCmd4Directive is not a function. Found: ${ typeof isCmd4Directive }` ); }); }); describe( `Test isCmd4Directive`, ( ) => { it( `isCmd4Directive should identify a Cmd4 directive`, ( ) => { let directive = "polling"; let rc = isCmd4Directive( directive ); assert.isString( rc.key, `Unexpected result for isCmd4Directive` ); assert.equal( rc.key, directive, `Expected result to be "polling"` ); }); it( `isCmd4Directive should NOT identify an uppercase type `, ( ) => { let directive = "Polling"; let rc = isCmd4Directive( directive ); assert.isNull( rc, `Expected result to be null` ); }); it( `isCmd4Directive should NOT identify an unknown type `, ( ) => { let directive = "Blast"; let rc = isCmd4Directive( directive ); assert.isNull( rc, `Expected result to be null` ); }); it( `isCmd4Directive should identify an uppercase type if upperCase is checked`, ( ) => { let directive = "Polling"; let rc = isCmd4Directive( directive, true ); assert.isString( rc.key, `Expected result to be a string` ); assert.equal( rc.key, "polling", `Expected result to be "polling"` ); }); }); ================================================ FILE: test/isDevDirective.js ================================================ 'use strict'; let { indexOfEnum } = require( "../utils/indexOfEnum" ); Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } }); let isDevDirective = require( "../utils/isDevDirective" ); let lcFirst = require( "../utils/lcFirst" ); var _api = new HomebridgeAPI(); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** TEST isDevDirectivw .************* describe( `Test isDevDirective import`, ( ) => { it( `isDevDirectivw should be a function `, ( ) => { assert.isFunction( isDevDirective, `isDevDirective is not a function. Found: ${ typeof isDevDirective }` ); }); }); describe( `Test isDevDirective`, ( ) => { it( `isDevDirective should identify a device `, ( ) => { let deviceName = CMD4_DEVICE_TYPE_ENUM.devEnumIndexToC( CMD4_DEVICE_TYPE_ENUM.Switch ); let rc = isDevDirective( deviceName ); assert.isString( rc.deviceName, `Unexpected result for isDevDirective` ); assert.equal( rc.deviceName, deviceName, `Expected result to be "on"` ); assert.equal( rc.devEnumIndex, CMD4_DEVICE_TYPE_ENUM.Switch, `UnExpected result` ); }); it( `isDevDirective should NOT identify an lowercase deviceName `, ( ) => { let deviceName = CMD4_DEVICE_TYPE_ENUM.devEnumIndexToC( CMD4_DEVICE_TYPE_ENUM.Switch ); let lcDeviceName = lcFirst( deviceName ); let rc = isDevDirective( lcDeviceName ); assert.isNull( rc.devEnumIndex, `Expected result to be null` ); }); it( `isDevDirective should NOT identify an unknown deviceName `, ( ) => { let deviceName = "Blast"; let rc = isDevDirective( deviceName ); assert.isNull( rc.devEnumIndex, `Expected result to be null` ); }); it( `isDevDirective should identify an lower case deviceName if lowerCase is checked`, ( ) => { let deviceName = CMD4_DEVICE_TYPE_ENUM.devEnumIndexToC( CMD4_DEVICE_TYPE_ENUM.Switch ); let lcDeviceName = lcFirst( deviceName ); let rc = isDevDirective( lcDeviceName, true ); assert.isString( rc.deviceName, `Unexpected result for isDevDirective` ); assert.equal( rc.deviceName, deviceName, `Expected result to be "on"` ); assert.equal( rc.devEnumIndex, CMD4_DEVICE_TYPE_ENUM.Switch, `UnExpected result` ); }); }); ================================================ FILE: test/isJSON.js ================================================ 'use strict'; var isJSON = require( "../utils/isJSON.js" ); describe( "Testing isJSON", ( ) => { it( "isJSON should be a function", ( ) => { assert.isFunction( isJSON, "isJSON is not a function" ); }); it( "isJSON should correctly identify a JSON object", ( ) => { let data = { "name": "John"}; let expectedResult = true; let result = isJSON( data ); assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isJSON should return false for a string", ( ) => { let data = "abcdef"; let expectedResult = false; let result = isJSON( data ); assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isJSON should return false for a number", ( ) => { let data = 12345; let expectedResult = false; let result = isJSON( data ); assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isJSON should return false for a float", ( ) => { let data = 3.1415; let expectedResult = false; let result = isJSON( data ); assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isJSON should return false for a boolean", ( ) => { let data = false; let expectedResult = false; let result = isJSON( data ); assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isJSON should return false for an array", ( ) => { let data = [1,2,3,4]; let expectedResult = false; let result = isJSON( data ); assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isJSON should return false for a undefined", ( ) => { let data = undefined; let expectedResult = false; let result = isJSON( data ); assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); }) ================================================ FILE: test/isNumeric.js ================================================ 'use strict'; var isNumeric = require( "../utils/isNumeric.js" ); describe( "Testing isNumeric", ( ) => { it( "isNumeric should be a function", ( ) => { assert.isFunction( isNumeric, "isNumeric is not a function" ); }); it( "isNumeric should correctly identify a string number", ( ) => { let data = "12345"; let expectedResult = true; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly identify a string float", ( ) => { let data = "3.1415"; let expectedResult = true; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly identify a number", ( ) => { let data = 12345; let expectedResult = true; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly identify a float", ( ) => { let data = 3.1415; let expectedResult = true; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly identify a negative float", ( ) => { let data = -3.1415; let expectedResult = true; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly identify a 0", ( ) => { let data = 0; let expectedResult = true; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( `isNumeric should correctly identify a "0"`, ( ) => { let data = "0"; let expectedResult = true; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly fail a character string", ( ) => { let data = "One"; let expectedResult = false; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly fail an undefined", ( ) => { let data = undefined; let expectedResult = false; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "isNumeric should correctly fail a null", ( ) => { let data = null; let expectedResult = false; let result = isNumeric( data ); assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); }) ================================================ FILE: test/loadPluginTest.js ================================================ "use strict"; // ***************** TEST LOADING ********************** var pluginModule = require( "../index" ); var CMD4_ACC_TYPE_ENUM = pluginModule.ACC_DATA.CMD4_ACC_TYPE_ENUM; var CMD4_DEVICE_TYPE_ENUM = pluginModule.DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM; // While the above method is better, It doesn't check that // the key values are sequential. // You cannot break out of forEach .... function getIndexOfValue( obj, value ) { let count = 0; let found = -1; Object.keys( obj ).forEach( function( key ) { // console.log( "Checking: " + key + " count: " + count + " obj[key]: " + obj[key] + " for: " + value + " t1: " + typeof obj[key] + " t2: " + typeof value ); if ( obj[key] == value && value == count ) { found = value } count+=1; }); return found; } // ************ TEST PLUGIN WAS Loaded Successfully ************** describe( "Testing load of index.js", ( ) => { it( "Testing require of index.js", ( ) => { expect( pluginModule ).not.to.be.a( "null", "loaded plugin was null" ); }); it( "index.js default initializer should be found", ( ) => { expect( pluginModule.default ).to.be.a( "function", "plugin has no default init function t: " + typeof pluginModule.default); }); }); // ************ TEST UNINITIALIZED PLUGIN ************** describe( "Testing uninitialized plugin", ( ) => { // DEVICE_TYPE Testing it( "CMD4_DEVICE_TYPE_ENUM is defined", ( ) => { expect( CMD4_DEVICE_TYPE_ENUM ).not.to.be.a( "null", "CMD4_DEVICE_TYPE_ENUM is null" ); }); it( "CMD4_DEVICE_TYPE_ENUM has EOL", ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).not.to.be.a( "null", "CMD4_DEVICE_TYPE_ENUM.EOL is null" ); }); it( "CMD4_DEVICE_TYPE_ENUM.EOL = " + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL, "CMD4_DEVICE_TYPE_ENUM.EOL. Expected: " + DEVICE_EOL + " found: " + CMD4_DEVICE_TYPE_ENUM.EOL ); }); it( "CMD4_DEVICE_TYPE_ENUM[ 0-" + CMD4_DEVICE_TYPE_ENUM.EOL + " ] to have a valid value", ( ) => { for ( let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++) { let keyIndex = getIndexOfValue( CMD4_DEVICE_TYPE_ENUM, index ); expect( keyIndex ).to.equal( index, "Expected value at index: " + index + " to be: " + index + " found: " + keyIndex ); } }); // ACC_TYPE Testing it( "CMD4_ACC_TYPE_ENUM is defined", ( ) => { expect( CMD4_ACC_TYPE_ENUM ).not.to.be.a( "null", "CMD4_ACC_TYPE_ENUM is null" ); }); it( "CMD4_ACC_TYPE_ENUM has EOL", ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).not.to.be.a( "null", "CMD4_ACC_TYPE_ENUM.EOL is null" ); }); it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL, "CMD4_ACC_TYPE_ENUM.EOL. Expected: " + ACC_EOL + " found: " + CMD4_ACC_TYPE_ENUM.EOL ); }); it( "CMD4_ACC_TYPE_ENUM[ 0-" + CMD4_ACC_TYPE_ENUM.EOL + " ] to have a valid value", ( ) => { for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ ) { let keyIndex = getIndexOfValue( CMD4_ACC_TYPE_ENUM, index ); expect( keyIndex ).to.equal( index, "Expected ACC ENUM at index: " + index + " to be: " + index + " found: " + keyIndex ); } }); }); ================================================ FILE: test/mocha-setup ================================================ // Define common functions and values for all unit tests. const which = require('which'); const path = require( "path" ); // Get the real path of homebridge instead of a dev dependancy, // which caused issues if you forget to update dependancies but // upgrade homebridge. const homebridgePath = which.sync( 'homebridge', { nothrow: true } ) let apiPath; if ( homebridgePath ) { let dirname = path.dirname( homebridgePath ); console.log( "Found homebridge in path %s", dirname ); apiPath = `${ dirname }/../lib/node_modules/homebridge/lib/api`; global.HomebridgeAPI = require( apiPath ).HomebridgeAPI; if ( ! global.HomebridgeAPI ) { console.log( "homebridgeAPI not available !!!" ); process.exit( 10 ); } // For serializing/deserializing arrays of accessories let platformAccessoryPath = `${ dirname }/../lib/node_modules/homebridge/lib/platformAccessory`; global.platformAccessory_1 = require( platformAccessoryPath ); } else { console.log( "homebridge not found !!!" ); process.exit( 10 ); } console.log( "Found api in %s", apiPath ); // IMPORTANT - ALL GLOBALS MUST BE DEFINED IN .eslintrc.json for lint to work global.fs = require( 'fs' ); global.assert = require( "chai" ).assert; global.expect = require( "chai" ).expect; global.sinon = require( "sinon" ); global.ACC_EOL = 255; global.DEVICE_EOL = 81; global.FORMAT_EOL = 11; global.UNITS_EOL = 5; global.PERMS_EOL = 9; global.ACCESS_EOL = 3; // These would be the uninitialized values, used for unit testing global.ACC_DATA = require( '../lib/CMD4_ACC_TYPE_ENUM' ); global.CMD4_ACC_TYPE_ENUM = ACC_DATA.CMD4_ACC_TYPE_ENUM; global.DEVICE_DATA = require( '../lib/CMD4_DEVICE_TYPE_ENUM' ); global.CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM; global.CHAR_DATA = require( `../lib/CMD4_CHAR_TYPE_ENUMS` ); global.CMD4_CHAR_TYPE_ENUMS = CHAR_DATA.CMD4_CHAR_TYPE_ENUMS; global.Logger = require("../utils/Logger"); // A true sleep ( blocking ). const moment = require( "moment" ); global.sleep = function( secondsToSleep = 1 ) { let sleepUntill = moment( ).add( secondsToSleep, 'seconds'); while( moment ( ).isBefore( sleepUntill ) ) { /* block the process */ } } global.accEnumIndexToC = function( index ) { return CMD4_ACC_TYPE_ENUM.properties[ index ].type; } global.devEnumIndexToC = function( index ) { return CMD4_DEVICE_TYPE_ENUM.properties[ index ].deviceName; } // How it's used // sleep( 10 ); global.cleanStatesDir = function( ) { const os = require( "os" ); const cmd4StateDir = os.homedir( ) + "/.homebridge/Cmd4Scripts/Cmd4States/" var glob = require( "glob" ); glob( cmd4StateDir + "Status_Device_*", null, function ( er, files ) { for ( var file of files ) { // To use the promise of unlink, it must be in an async function // so put it in one. Why not unLinkSync, because for whatever reason // some files were notbremoved synchronously. ( async( ) => { await fs.unlink( file, function( err, result ) { if ( err && err.code != 'ENOENT' ) console.log( 'file not removed err: ' + err + " result: " + result ); }); }); } }) } ================================================ FILE: test/pollingTest.js ================================================ #!node // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); let constants = require( "../cmd4Constants" ); let Cmd4Accessory = require( "../Cmd4Accessory" ).Cmd4Accessory; let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; let Cmd4PriorityPollingQueue = require( "../Cmd4PriorityPollingQueue" ).Cmd4PriorityPollingQueue; // Hierarchy variables let HV = require( "../utils/HV" ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); describe('Testing Cmd4Accessory polling', ( ) => { beforeEach(function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach(function( ) { if (this.currentTest.state == 'failed') { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); }); } // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; }); let cmd4Accessory; it( "Test if Cmd4Accessory exists", function ( ) { expect( Cmd4Accessory ).not.to.be.a( "null", "Cmd4Accessory was null" ); }); it( "Test init Cmd4Accessory has default polling messages", function( done ) { let config = { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, polling: true, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "Cmd4Accessory is not an instance of Cmd4Accessory" ); assert.include( log.logBuf, "[90mPolling config is Default Polling. Nothing to check for unset polling characteristics\u001b" , `Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, "[90mSetting up which characteristics will be polled for MySwitch\u001b" , `Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "" , `Unexpected stderr: ${ log.errBuf }` ); done( ); }); it('cmd4Accessory determineCharacteristicsToPollForAccessory works for non queued polling characteristics.', ( done ) => { let config = { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); cmd4Accessory["polling"] = [ { characteristic: "on", "timeout":3000, "interval": 1 } ]; cmd4Accessory.determineCharacteristicsToPollForAccessory( cmd4Accessory ); assert.equal( Object.keys(cmd4Accessory.listOfPollingCharacteristics).length, 1, `Incorrect number of listOfPollingCharacteristics` ); let queue = settings.listOfCreatedPriorityQueues[ "Q:MySwitch" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PriorityPollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 1, `Incorrect number of lowPriorityQueue Entries` ); assert.include( log.logBuf, `[90mSetting up accessory: MySwitch for polling of: On timeout: 3000 interval: 1000 queueName: "Q:MySwitch"\u001b` , `Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "" , `Unexpected stderr: ${ log.errBuf }` ); done( ); }); it('cmd4Accessory characteristic polling interval/timeout changes recognized.', ( done ) => { let config = { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); cmd4Accessory["polling"] = [ { characteristic: "on", "timeout":1234, "interval": 99 } ]; cmd4Accessory.determineCharacteristicsToPollForAccessory( cmd4Accessory ); assert.include( log.logBuf, `[90mSetting up accessory: MySwitch for polling of: On timeout: 1234 interval: 99000 queueName: "Q:MySwitch"\u001b` , `Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "" , `Unexpected stderr: ${ log.errBuf }` ); done( ); }); it('cmd4Accessory characteristic polling recognizes default accessory interval/timeout.', ( done ) => { let config = { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", timeout: 5500, interval: 44, on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); cmd4Accessory["polling"] = [ { characteristic: "on" } ]; cmd4Accessory.determineCharacteristicsToPollForAccessory( cmd4Accessory ); assert.include( log.logBuf, `[90mSetting up accessory: MySwitch for polling of: On timeout: 5500 interval: 44000 queueName: "Q:MySwitch"\u001b` , `Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "" , `Unexpected stderr: ${ log.errBuf }` ); done( ); }); it('cmd4Accessory characteristic polling recognizes parent interval/timeout.', ( done ) => { let config = { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } let hV = new HV(); hV.timeout = 77000; hV.interval = 22000; let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1, "hV": hV }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); cmd4Accessory["polling"] = [ { characteristic: "on" } ]; cmd4Accessory.determineCharacteristicsToPollForAccessory( cmd4Accessory ); assert.include( log.logBuf, `[90mSetting up accessory: MySwitch for polling of: On timeout: 77000 interval: 22000 queueName: "Q:MySwitch"\u001b` , `Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "" , `Unexpected stderr: ${ log.errBuf }` ); done( ); }); it('cmd4Accessory default polling recognizes parent interval/timeout.', ( done ) => { let config = { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } let hV = new HV(); hV.timeout = 88000; hV.interval = 41000; let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1, "hV": hV }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); cmd4Accessory["polling"] = true; cmd4Accessory.determineCharacteristicsToPollForAccessory( cmd4Accessory ); assert.include( log.logBuf, `[90mAdding MySwitch On record.timeout: 88000 record.interval: 41000 to Polled Queue Q:MySwitch\u001b` , `Incorrect stdout: ${ log.logBuf }` ); assert.equal( log.errBuf, "" , `Unexpected stderr: ${ log.errBuf }` ); done( ); }); it('cmd4Accessory characteristic polling short timeout generates warning.', ( done ) => { let config = { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, polling: [ { characteristic: "on", timeout: 2, interval: 99 } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" } let parentInfo={ "CMD4": constants.PLATFORM, "LEVEL": -1 }; const log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); cmd4Accessory = new Cmd4Accessory( log, config, _api, [ ], parentInfo ); assert.include( log.logBuf, `[90mSetting up accessory: MySwitch for polling of: On timeout: 2 interval: 99000 queueName: "Q:MySwitch"` , `Incorrect stdout: ${ log.logBuf }` ); assert.include( log.errBuf, `[33mTimeout for: MySwitch is in milliseconds. A value of: 2 seems pretty low.\u001b[39m` , `Incorrect stderr: ${ log.errBuf }` ); done( ); }); it('Cmd4Platform starts polling of 1 characteristic.', ( done ) => { let platformConfig = { accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, polling: [ { characteristic: "on", interval: 310 } ], state_cmd: "node ./Extras/Cmd4Scripts/Examples/AnyDevice" }] } let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of listOfCreatedPriorityPollingQueues` ); let queue = settings.listOfCreatedPriorityQueues[ "Q:MySwitch" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PriorityPollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 1, `Incorrect number of lowPriorityQueue Entries` ); // For unit testing, start te polling now cmd4Platform.startPolling( 0 ); // Staggered Polling takes 3 seconds to start setTimeout( () => { assert.include( log.logBuf, `[90mKicking off polling for: MySwitch On interval:310000, staggered:3000\u001b` , `Incorrect stdout: ${ log.logBuf }` ); assert.include( log.logBuf, "Started staggered kick off of 1 polled characteristics" , `Incorrect stdout: ${ log.logBuf }` ); // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); }); done( ); }, 6000); }).timeout(7000); }); ================================================ FILE: test/sanityTests ================================================ test/Cmd4Storage.js test/HV.js test/cmd4Constants.js test/CMD4_CHAR_TYPE_ENUMS.js test/CMD4_ACC_TYPE_ENUM.js test/CMD4_DEVICE_TYPE_ENUM.js test/isAccDirective.js test/isDevDirective.js test/isCmd4Directive.js test/configTest.js test/loadPluginTest.js test/Cmd4Accessory.js test/internalRelatedTargetTests.js test/Cmd4Platform.js test/Cmd4AccessoryGetValue.js test/Cmd4AccessorySetValue.js test/Cmd4Mode.js test/Cmd4PriorityPollingQueue.js test/initPluginTest.js test/fakeGato.js test/pollingTest.js test/Cmd4PlatformRestartTests.js ================================================ FILE: test/systemTest.js ================================================ #!node // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); let constants = require( "../cmd4Constants" ); let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; let Cmd4PriorityPollingQueue = require( "../Cmd4PriorityPollingQueue" ).Cmd4PriorityPollingQueue; // Duplicated from Cmd4PriorityPollingQueue.js let HIGH_PRIORITY_SET = 0; //let HIGH_PRIORITY_GET = 1; //let LOW_PRIORITY_GET = 2; var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); describe('WoRM - Testing Cmd4PriorityPollingQueue recovery correction', ( ) => { before( ( ) => { sinon.stub( process, `exit` ); cleanStatesDir(); }); after( ( ) => { process.exit.restore( ); }); beforeEach( function( ) { settings.arrayOfAllStaggeredPollingCharacteristics = [ ]; settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; // MaxListenersExceededWarning: Possible EventEmitter memory leak detected _api.removeAllListeners(); }); it( `Worm Test HighPriority "Set" Entry passes without errors`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], statusMsg: true, accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "./test/echoScripts/justExitWithRCof0" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // Not to be turned on !! let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( 0, log.errLineCount, ` Unexpected number of stderr lines: ${ log.errBuf }` ); log.reset( ); log.setOutputEnabled( true ); log.setDebugEnabled( false ); // The queue setValue will just return successful // and do the actual setValue later var dummyCallback = function( rc ) { assert.equal( rc, 0, ` setValue incorrect rc: ${ rc }` ); }; // Call the setValue bound function, which is prioritySetValue //cmd4SwitchAccessory.service.getCharacteristic( // CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ] // .characteristic ).setValue( true, dummyCallback ); cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: 1, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_SET, cmd4PriorityPollingQueue ); // Only 1 line since statusMsg: true assert.equal( 1, log.logLineCount, ` Unexpected number of stdout lines: ${ log.logBuf }` ); assert.include( log.logBuf, `[34mSetting MySwitch On\u001b` , `expected stdout: ${ log.logBuf }` ); assert.equal( 0, log.errLineCount, ` Unexpected number of stderr lines: ${ log.errBuf }` ); assert.equal( log.errBuf, "", ` Unexpected stderr: ${ log.errBuf }` ); done( ); }); it( `Worm Test HighPriority "Get" Entry passes without errors`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "./test/echoScripts/echo_true" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // Not to be turned on !! let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.queueRetryCount, 0, `Incorrect queueRetryCount` ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); queue.lowPriorityQueue = []; //Make sure no polling happens assert.equal( queue.lowPriorityQueue.length, 0, `Incorrect number of low level polling characteristics` ); Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); queue.pauseTimerTimeout = 0; let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); assert.equal( 0, log.errLineCount, ` Unexpected number of stderr lines: ${ log.errBuf }` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // Not to be turned on !! // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; var allDoneCallback = function( allDoneCount ) { assert.equal( allDoneCount, 0, ` startQueue incorrect allDoneCount` ); }; assert.equal( cmd4PriorityPollingQueue.queueType, constants.DEFAULT_QUEUE_TYPE, ` incorrect default queue type. Should be WoRm` ); cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: dummyCallback } ); //cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); cmd4PriorityPollingQueue.startQueue( cmd4PriorityPollingQueue, allDoneCallback ); // Wait for the entry in the Queue to be processed setTimeout( ( ) => { assert.equal( 0, log.logLineCount, ` Unexpected number of stdout lines: ${ log.logBuf }` ); assert.equal( 0, log.errLineCount, ` Unexpected number of stderr lines: ${ log.errBuf }` ); assert.equal( log.errBuf, "", ` Unexpected stderr: ${ log.errBuf }` ); // A quick way to stop the queue. For whatever reason, if the above fails, // the testcase will not do this command and the testcase runs forever cmd4SwitchAccessory.queue.inProgressSets = 10; done( ); }, 2500 ); }).timeout( 3000 ); it( `Worm Test lowPriority "Get" Entry passes without errors`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "./test/echoScripts/echo_true" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // Not to be turned on !! let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); //cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.On, "On", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); //cmd4PriorityPollingQueue.addLowPriorityGetPolledQueueEntry( cmd4SwitchAccessory, CMD4_ACC_TYPE_ENUM.Active, "Active", constants.DEFAULT_INTERVAL, constants.DEFAULT_TIMEOUT ); //assert.equal( cmd4PriorityPollingQueue.lowPriorityQueue.length, 4, `Polled Get added to low prority queue` ); assert.equal( 0, log.errLineCount, ` Unexpected number of stderr lines: ${ log.errBuf }` ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( false ); // Not to be turned on !! // Process 1 entry from the polling queue cmd4PriorityPollingQueue.processEntryFromLowPriorityQueue( cmd4PriorityPollingQueue.lowPriorityQueue[ 0 ] ); setTimeout( () => { assert.equal( 0, log.logLineCount, ` Unexpected number of stdout lines: ${ log.logBuf }` ); assert.equal( 0, log.errLineCount, ` Unexpected number of stderr lines: ${ log.errBuf }` ); assert.equal( log.errBuf, "", ` Unexpected stderr: ${ log.errBuf }` ); done( ); }, 1000); }).timeout( 3000 ); }); ================================================ FILE: test/testAdvAirGetSet.js ================================================ #!node // Settings, Globals and Constants let settings = require( "../cmd4Settings" ); let constants = require( "../cmd4Constants" ); let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform; let Cmd4PriorityPollingQueue = require( "../Cmd4PriorityPollingQueue" ).Cmd4PriorityPollingQueue; // Duplicated from Cmd4PriorityPollingQueue.js //let HIGH_PRIORITY_SET = 0; let HIGH_PRIORITY_GET = 1; //let LOW_PRIORITY_GET = 2; var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_DEVICE_TYPE_ENUM", ( ) => { it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( "Quick Test of CMD4_ACC_TYPE_ENUM", ( ) => { it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); describe('AdvAir test of Cmd4PriorityPollingQueue polling', ( ) => { before( ( ) => { sinon.stub( process, `exit` ); cleanStatesDir(); }); after( ( ) => { process.exit.restore( ); }); beforeEach( function( ) { settings.listOfCreatedPriorityQueues = { }; }); afterEach( function( ) { // Clear any timers created for any polling queue Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) => { let queue = settings.listOfCreatedPriorityQueues[ queueName ]; Object.keys(queue.listOfRunningPolls).forEach( (key) => { let timer = queue.listOfRunningPolls[ key ]; clearTimeout( timer ); }); clearTimeout( queue.pauseTimer ); }); // Put back the polling queues settings.listOfCreatedPriorityQueues = { }; // MaxListenersExceededWarning: Possible EventEmitter memory leak detected _api.removeAllListeners(); }); it( `AdvAir - Test "Get" Cmd From High Priority Queue`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./test/AdvAir.sh" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: cmd4SwitchAccessory.hV.timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Get" entry: ${ CMD4_ACC_TYPE_ENUM.On } isUpdate: false length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch cmd: node ./test/AdvAir.sh Get "MySwitch" 'On' timeout: 60000` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: On function for: MySwitch returned: 1` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); it( `AdvAir - Test "Get" Cmd Quotes From High Priority Queue`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "Mia'sSwitch", displayName: "Mia'sSwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./test/AdvAir.sh" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue getValue will just return the cached value // and call updateValue later var dummyCallback = function( rc, result ) { assert.equal( rc, 0, ` getValue incorrect rc: ${ rc }` ); assert.equal( result, true, ` getValue incorrect result: ${ result }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: false, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: cmd4SwitchAccessory.hV.timeout, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: null, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Get" entry: ${ CMD4_ACC_TYPE_ENUM.On } isUpdate: false length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: Mia'sSwitch cmd: node ./test/AdvAir.sh Get "Mia'sSwitch" 'On' timeout: 60000` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90mgetValue: On function for: Mia'sSwitch returned: 1` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); it( `AdvAir - Test "Set" Cmd From High Priority Queue`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "MySwitch", displayName: "MySwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./test/AdvAir.sh" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue setValue will just return successful // and do the actual setValue later var dummyCallback = function( rc ) { assert.equal( rc, 0, ` setValue incorrect rc: ${ rc }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: true, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Set" entry: ${ CMD4_ACC_TYPE_ENUM.On } length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90msetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: MySwitch 1 cmd: node ./test/AdvAir.sh Set "MySwitch" 'On' '1'` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); it( `AdvAir - Test "Set" Cmd Quotes From High Priority Queue`, ( done ) => { let platformConfig = { queueTypes: [ { queue: "A", queueType: "WoRm" } ], accessories: [ { name: "Mia'sSwitch", displayName: "Mia'sSwitch", statusMsg: true, type: "Switch", on: 0, active: 0, queue: "A", polling: [ { characteristic: "on" }, { characteristic: "active" } ], state_cmd: "node ./test/AdvAir.sh" }] }; let log = new Logger( ); log.setBufferEnabled( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); let cmd4Platform = new Cmd4Platform( log, platformConfig, _api ); expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" ); cmd4Platform.discoverDevices( ); assert.equal( Object.keys(settings.listOfCreatedPriorityQueues).length, 1, `Incorrect number of polling queues created` ); let queue = settings.listOfCreatedPriorityQueues[ "A" ]; expect( queue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "queue is not an instance of Cmd4PollingQueue" ); assert.equal( queue.lowPriorityQueue.length, 2, `Incorrect number of low level polling characteristics` ); let cmd4SwitchAccessory = cmd4Platform.createdCmd4Accessories[0]; let cmd4PriorityPollingQueue = cmd4SwitchAccessory.queue; expect( cmd4PriorityPollingQueue ).to.be.a.instanceOf( Cmd4PriorityPollingQueue, "Cmd4PriorityPollingQueue is not an instance of Cmd4Accessory" ); log.reset( ); log.setOutputEnabled( false ); log.setDebugEnabled( true ); // The queue setValue will just return successful // and do the actual setValue later var dummyCallback = function( rc ) { assert.equal( rc, 0, ` setValue incorrect rc: ${ rc }` ); }; cmd4PriorityPollingQueue.highPriorityQueue.push( { [ constants.IS_SET_lv ]: true, [ constants.QUEUE_GET_IS_UPDATE_lv ]: false, [ constants.ACCESSORY_lv ]: cmd4SwitchAccessory, [ constants.ACC_TYPE_ENUM_INDEX_lv ]: CMD4_ACC_TYPE_ENUM.On, [ constants.CHARACTERISTIC_STRING_lv ]: "On", [ constants.TIMEOUT_lv ]: constants.DEFAULT_TIMEOUT, [ constants.STATE_CHANGE_RESPONSE_TIME_lv ]: null, [ constants.VALUE_lv ]: true, [ constants.CALLBACK_lv ]: dummyCallback } ); cmd4PriorityPollingQueue.processQueueFunc( HIGH_PRIORITY_GET, cmd4PriorityPollingQueue ); // Wait for the Queue to be processed setTimeout( ( ) => { assert.include( log.logBuf, `[90mProcessing high priority queue "Set" entry: ${ CMD4_ACC_TYPE_ENUM.On } length: 0` , `expected stdout: ${ log.logBuf }` ); assert.include( log.logBuf, `[90msetValue: accTypeEnumIndex:( ${ CMD4_ACC_TYPE_ENUM.On } )-"On" function for: Mia'sSwitch 1 cmd: node ./test/AdvAir.sh Set "Mia'sSwitch" 'On' '1'` , `expected stdout: ${ log.logBuf }` ); done( ); }, 1000 ); }); }); ================================================ FILE: test/testOurConfig.json.js ================================================ 'use strict'; var fs = require('fs'), Parser = require('jsonparse'); var json = fs.readFileSync("Extras/config.json"); describe( "Testing our config.json", ( ) => { it( "isJSON should be a function", ( ) => { assert.isFunction( Parser, "Parser is not a function" ); }); it( "If our config.json is valid", ( ) => { JSON.parse(json); var p = new Parser(); p.onError = function (value) { assert("Our config.json is invalid value:", value); }; p.write( json ); }); }) ================================================ FILE: test/transposeCMD4Props.js ================================================ 'use strict'; // ***************** TEST LOADING ********************** var { transposeConstantToValidValue, transposeValueToValidConstant, extractKeyValue } = require( "../utils/transposeCMD4Props" ); // ***************** TEST Plugin Initialized Variables *************** describe( `Initializing our CMD4 Libraries`, ( ) => { } ); var _api = new HomebridgeAPI( ); // object we feed to Plugins // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); // ******** QUICK TEST CMD4_ACC_TYPE_ENUM ************* describe( `Quick Test load of CMD4_ACC_TYPE_ENUM`, ( ) => { it( `CMD4_ACC_TYPE_ENUM.EOL = ${ ACC_EOL }`, ( ) => { expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL ); }); }); // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM ************* describe( `Quick Test load of CMD4_DEVICE_TYPE_ENUM`, ( ) => { it( `CMD4_DEVICE_TYPE_ENUM.EOL =${ DEVICE_EOL }`, ( ) => { expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL ); }); }); // ******** TEST extractKeyValue.************* describe( `Testing extractKeyValue`, ( ) => { it( `extractKeyValue should be a function`, ( ) => { expect( extractKeyValue).to.be.a( "function", `extractKeyValue is not a function. found: ${ typeof extractKeyValue }`); }); it( `extractKeyValue should return correct key`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.CurrentDoorState; let expectedKey = "CLOSING"; let value = 3; let result = extractKeyValue( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].validValues, value ); expect( result ).to.equal( expectedKey, `Test 1 extractKeyValue( ${ value } ) returned:${ result } expected:${ expectedKey }` ); }); it( `extractKeyValue should return undefined for no value`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.CurrentDoorState; let expectedKey = undefined; let value = undefined; let result = extractKeyValue( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].validValues, value ); expect( result ).to.equal( expectedKey, `Test 2 extractKeyValue( ${ value } ) returned:${ result } expected:${ expectedKey }` ); }); it( `extractKeyValue For last index should reterun last key`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.CurrentDoorState; let keys = Object.keys( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].validValues ); let keyCount = keys.length; let expectedKey = keys[keyCount -1 ]; let value = keyCount -1; let result = extractKeyValue( CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].validValues, value ); expect( result ).to.equal( expectedKey, `Test 3 extractKeyValue( ${ value } ) returned:${ result } expected:${ expectedKey }` ); }); }); describe( `Testing transposeConstantToValidValue`, ( ) => { it( `transposeConstantToValidValue should return originalfor no translation`, ( ) => { // has {} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.AccessControlLevel; let constantToBeChecked = "DUMMY"; let expectedResult = constantToBeChecked; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, constantToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeConstantToValidValue from ${ constantToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeConstantToValidValue should return correct constant for 0-INACTIVE `, ( ) => { // has { "INACTIVE" & "ACTIVE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Active; let constantToBeChecked = "INACTIVE"; let expectedResult = 0; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, constantToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeConstantToValidValue from ${ constantToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeConstantToValidValue should return correct constant for 0-iNACTIVE`, ( ) => { // has { "INACTIVE" & "ACTIVE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Active; let constantToBeChecked = "INACTIVE"; let expectedResult = 0; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, constantToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeConstantToValidValue from ${ constantToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeConstantToValidValue should return correct constant for 1-ACTIVE `, ( ) => { // has { "INACTIVE" & "ACTIVE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Active; let constantToBeChecked = "ACTIVE"; let expectedResult = 1; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, constantToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeConstantToValidValue from ${ constantToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeConstantToValidValue should return correct value 1 for constant "TRUE"`, ( ) => { // has { "FALSE" & "TRUE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.AdministratorOnlyAccess; let constantToBeChecked = "TRUE"; let expectedResult = 1; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, constantToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeConstantToValidValue from ${ constantToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeConstantToValidValue should return correct value 4 for constant "INTERUPTED"`, ( ) => { // has { "FALSE" & "TRUE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.AdministratorOnlyAccess; let constantToBeChecked = "TRUE"; let expectedResult = 1; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, constantToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeConstantToValidValue from ${ constantToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); }); describe( `Testing transposeValueToValidConstant`, ( ) => { it( `transposeValueToValidConstant should return original for no translation`, ( ) => { // has {} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.AccessControlLevel; let valueToBeChecked = "DUMMY"; let expectedResult = valueToBeChecked; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, valueToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeValueToValidConstant from ${ valueToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeValueToValidConstant should return correct constant for 0-INACTIVE`, ( ) => { // has { "INACTIVE" & "ACTIVE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Active; let valueToBeChecked = 0; let expectedResult = "INACTIVE"; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, valueToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeValueToValidConstant from ${ valueToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeValueToValidConstant should return correct constant for 1-ACTIVE`, ( ) => { // has { "INACTIVE" & "ACTIVE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Active; let valueToBeChecked = 1; let expectedResult = "ACTIVE"; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, valueToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeValueToValidConstant from ${ valueToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); it( `transposeValueToValidConstant should return correct constant for value 1`, ( ) => { // has { "FALSE" & "TRUE"} for validValues let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.AdministratorOnlyAccess; let valueToBeChecked = 1; let expectedResult = "TRUE"; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, valueToBeChecked ); expect( transposed ).to.equal( expectedResult, `transposeValueToValidConstant from ${ valueToBeChecked } returned ${ transposed } instead of ${ expectedResult } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); }); }); // TEST all Possible validValues for transposeValueToValidConstant describe( `Testing all Possible validValues for transposeValueToValidConstant`, ( ) => { for ( let index = 0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ ) // for ( let index = CMD4_ACC_TYPE_ENUM.EOL -1 ; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ ) { if ( Array.isArray( CMD4_ACC_TYPE_ENUM.properties[ index ].validValues )) { let numberOfValues = CMD4_ACC_TYPE_ENUM.properties[ index ].values.length; for ( let vindex = 0; vindex < numberOfValues; vindex ++ ) { // testTransposeValueTo( CMD4_ACC_TYPE_ENUM.properties, index, // CMD4_ACC_TYPE_ENUM.properties[ index ].validValues[ vindex ], true ); testTransposeValueTo( CMD4_ACC_TYPE_ENUM.properties, index, vindex, true ); } } else { //testTransposeValueTo( CMD4_ACC_TYPE_ENUM.properties, index, // CMD4_ACC_TYPE_ENUM.properties[ index ].validValues, true ); testTransposeValueTo( CMD4_ACC_TYPE_ENUM.properties, index, index, true ); } } // Test a failing one //testTransposeValueTo( CMD4_ACC_TYPE_ENUM.properties, 0, {"one": "1", "two": "2"}, false ); testTransposeValueTo( CMD4_ACC_TYPE_ENUM.properties, 0, 7, false ); }); // TEST all Possible transposeConstantToValidValue describe( `Testing all Possible transposeConstantToValidValue`, ( ) => { for ( let index =0 ; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ ) { if ( Array.isArray( CMD4_ACC_TYPE_ENUM.properties[ index ].validValues )) { let numberOfValues = CMD4_ACC_TYPE_ENUM.properties[ index ].validValues.length; for ( let vindex = 0; vindex < numberOfValues; vindex ++ ) { //testTransposeConstantTo( CMD4_ACC_TYPE_ENUM.properties, index, // CMD4_ACC_TYPE_ENUM.properties[ index ].validValues[ vindex ], true ); testTransposeConstantTo( CMD4_ACC_TYPE_ENUM.properties, index, vindex, true ); } } else { //testTransposeConstantTo( CMD4_ACC_TYPE_ENUM.properties, index, // CMD4_ACC_TYPE_ENUM.properties[ index ].validValues, true ); testTransposeConstantTo( CMD4_ACC_TYPE_ENUM.properties, index, index, true ); } } // Test a failing one //testTransposeConstantTo( CMD4_ACC_TYPE_ENUM.properties, 0, {"one": "1", "two": "2"}, false ); testTransposeConstantTo( CMD4_ACC_TYPE_ENUM.properties, 0, 7, false ); // Test constantToValidValue CELSIUS (Correctly spelled ) it( `transposeConstantToValidValue for correctly spelled "CELSIUS" should return expected value`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, "CELSIUS"); expect( transposed ).to.equal( _api.hap.Characteristic.TemperatureDisplayUnits.CELSIUS, `transposeConstantTo from CELSIUS returned incorrect value` ); }); // Test constantToValidValue mispelled CELSIUS - CELcIUS it( `transposeConstantToValidValue for incorrectly spelled "CELciUS" should fail`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits; let badInput = "CELcIUS"; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, badInput ); expect( transposed ).to.equal( badInput, `transposeConstantTo from ${ badInput } returned incorrect value` ); }); it( `transposeConstantToValidValue for: "FAHRENHEIT" should return expected value`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, "FAHRENHEIT"); expect( transposed ).to.equal( _api.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT, `transposeConstantTo from "FAHRENHEIT" returned incorrect value` ); }); it( `transposeValueToValidConstant for: 1 should return expected "FAHRENHEIT"`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, _api.hap.Characteristic.TemperatureDisplayUnits.FAHRENHEIT); expect( transposed ).to.equal( "FAHRENHEIT", `transposeValueTo from 1 returned incorrect constant` ); }); it( `transposeValueToValidConstant for: 3 should fail as expected`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits; let badInput = 3; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, badInput); expect( transposed ).to.equal( badInput, `transposeValueTo from ${ badInput } returned incorrect value` ); }); it( `transposeValueToValidConstant SENDING "FAHRENHEIT", should return "FAHRENHEIT" and say already transposed`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, "FAHRENHEIT"); expect( transposed ).to.equal( "FAHRENHEIT", `transposeValueTo from 1 returned incorrect constant` ); }); it( `transposeConstantToValidValue SENDING "1", should return "1" (FAHRENHEIT) and say already transposed`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.TemperatureDisplayUnits; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, 1); expect( transposed ).to.equal( 1, `transposeConstantTo from 1 returned incorrect value` ); }); it( `transposeConstantToValidValue To characteristic with no valid values should fail approperiately.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.ActiveIdentifier; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, 1); expect( transposed ).to.equal( 1, `transposeConstantTo from 1 returned incorrect value` ); }); it( `transposeValueToValidConstant To characteristic with no valid values should fail approperiately.`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.ActiveIdentifier; let transposed = transposeValueToValidConstant( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, "BLAST"); expect( transposed ).to.equal( "BLAST", `transposeValueTo from "BLAST" returned incorrect value` ); }); it( `transposeConstantToValidValue for: "1" should pass as expected`, ( ) => { let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Active; let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, "1"); expect( transposed ).to.equal( "1", `transposeValueTo from "1" returned incorrect value` ); }); it( `transposeConstantToValidValue for: "ENABLED" should fail as expected`, ( ) => { // Active allows "ACTIVE" and "INACTIVE", not "ENABLED" let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.Active; let data = "ENABLED" let transposed = transposeConstantToValidValue( CMD4_ACC_TYPE_ENUM.properties, accTypeEnumIndex, data); expect( transposed ).to.equal( data, `transposeValueTo from "${ data } " returned incorrect value: ${ transposed }` ); }); }); // ***************** Common way of testing functions above ********************** function testTransposeConstantTo( CMD4_ENUM_properties_obj, accTypeEnumIndex, vindex, shouldPass ) { // if ( ! obj ) if ( ! CMD4_ENUM_properties_obj[accTypeEnumIndex].validValues ) { // Nothing to do return; } //let numberOfKeyValuePairs = Object.keys( obj ); let numberOfKeyValuePairs = Object.keys( CMD4_ENUM_properties_obj[accTypeEnumIndex].validValues ); if ( numberOfKeyValuePairs < 0 ) { // Nothing to do return; } //for ( let key in obj ) for ( let key in CMD4_ENUM_properties_obj[accTypeEnumIndex].validValues ) { let valueThatShouldBeReturned = CMD4_ENUM_properties_obj[accTypeEnumIndex].validValues[ key ]; let transposed = transposeConstantToValidValue( CMD4_ENUM_properties_obj, accTypeEnumIndex, key); it( `transposeConstantToValidValue for: ${ CMD4_ENUM_properties_obj[accTypeEnumIndex].type } should return expected value: ${ transposed } for key: ${ key }`, ( ) => { if ( shouldPass ) { expect( valueThatShouldBeReturned ).to.equal( transposed, `transposeConstantTo from ${ key } returned ${ transposed } instead of ${ valueThatShouldBeReturned } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); } else { expect( valueThatShouldBeReturned ).to.not.equal( transposed, `transposeConstantTo from ${ key } returned ${ transposed } instead of ${ valueThatShouldBeReturned } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); } }); } } function testTransposeValueTo( CMD4_ENUM_properties_obj, accTypeEnumIndex, vindex, shouldPass ) { //let numberOfKeyValuePairs = Object.keys( obj ); let numberOfKeyValuePairs = Object.keys( CMD4_ENUM_properties_obj[accTypeEnumIndex].validValues ); if ( numberOfKeyValuePairs < 0 ) { // Nothing to do return; } // for ( let key in obj ) for ( let key in CMD4_ENUM_properties_obj[accTypeEnumIndex].validValues ) { //let constantToBeChecked = obj[ key ]; let constantToBeChecked = CMD4_ENUM_properties_obj[accTypeEnumIndex].validValues[ key ]; let transposed = transposeValueToValidConstant( CMD4_ENUM_properties_obj, accTypeEnumIndex, constantToBeChecked ); //it( `transposeValueToValidConstant should return expected constant`, ( ) => it( `transposeValueToValidConstant should return expected constant: ${ constantToBeChecked } for key: ${ key }`, ( ) => { if ( shouldPass ) { expect( key ).to.equal( transposed, `transposeValueTo from ${ constantToBeChecked } returned ${ transposed } instead of ${ key } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); } else { expect( key ).to.not.equal( transposed, `transposeValueTo from ${ constantToBeChecked } returned ${ transposed } instead of ${ key } for ${ CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type }` ); } }); } } ================================================ FILE: test/trueTypeOf.js ================================================ 'use strict'; var trueTypeOf = require( "../utils/trueTypeOf.js" ); describe( "Testing trueTypeOf", ( ) => { it( "trueTypeOf should be a function", ( ) => { assert.isFunction( trueTypeOf, "trueTypeOf is not a function" ); }); it( "trueTypeOf should correctly identify a String object", ( ) => { let data = "Cmd4"; let expectedResult = String; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a Boolean false object", ( ) => { let data = false; let expectedResult = Boolean; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a Boolean true object", ( ) => { let data = true; let expectedResult = Boolean; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a Number object", ( ) => { let data = 42; let expectedResult = Number; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a Array object", ( ) => { let data = [ 1, 2, 3 ]; let expectedResult = Array; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a polling config", ( ) => { let data = [ { "characteristic": "active", "timeout": 5, "interval": 3}, { "characteristic": "On", "timeout": 8, "interval": 4} ]; let expectedResult = Array; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify an empty object ", ( ) => { let data = undefined let expectedResult = undefined; let result = trueTypeOf( ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a undefined object ", ( ) => { let data = undefined let expectedResult = undefined; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a null object", ( ) => { let data = null; let expectedResult = null; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a 0 as a Number", ( ) => { let data = 0; let expectedResult = Number; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a \"0\" as a String", ( ) => { let data = "0"; let expectedResult = String; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); it( "trueTypeOf should correctly identify a \"1\" as a String", ( ) => { let data = "0"; let expectedResult = String; let result = trueTypeOf( data ); assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult ); }); }) ================================================ FILE: test/versionChecker.js ================================================ 'use strict'; const { isUpgrade, getLatestVersion, isVersionNewerThanPackagedVersion, getPackagedVersion } = require( "../utils/versionChecker" ); describe( `Testing versionChecker init`, ( ) => { it( `isUpgrade should be a function`, ( ) => { assert.isFunction( isUpgrade, `isUpgrade is not a function` ); }); it( `getLatestVersion should be a function`, ( ) => { assert.isFunction( getLatestVersion, `getLatestVersion is not a function` ); }); it( `isVersionNewerThanPackagedVersion should be a function`, ( ) => { assert.isFunction( isVersionNewerThanPackagedVersion, `isVersionNewerThanPackagedVersion is not a function` ); }); it( `getPackagedVersion should be a function`, ( ) => { assert.isFunction( getPackagedVersion, `getPackagedVersion is not a function` ); }); }); describe( `Testing versionChecker functionality`, ( ) => { it( `getPackagedVersion should return an string`, ( ) => { let result = getPackagedVersion( ); assert.isString( result, `getPackagedVersion failed: ${ typeof result }` ); }); it( `getLatestVersion should return an string`, async ( ) => { let result = await getLatestVersion( ); assert.isString( result, `getLatestVersion failed: ${ typeof result }` ); }).timeout(5000); it( `isVersionNewerThanPackagedVersion should return true for a high version`, ( ) => { let result = isVersionNewerThanPackagedVersion( "9.0.0" ); assert.isTrue( result, `isVersionNewerThanPackagedVersion expected: true: found: ${ result }` ); }); it( `isVersionNewerThanPackagedVersion should return false for a lower version`, ( ) => { let result = isVersionNewerThanPackagedVersion( "1.0.0" ); assert.isFalse( result, `isVersionNewerThanPackagedVersion expected: false: found: ${ result }` ); }); it( `isVersionNewerThanPackagedVersion should return false for same version`, async ( ) => { let latest = await getLatestVersion( ); let result = isVersionNewerThanPackagedVersion( latest ); assert.isFalse( result, `isVersionNewerThanPackagedVersion expected: false: found: ${ result }` ); }).timeout(5000); }) ================================================ FILE: tools/Cmd4AccDocGenerator ================================================ #!/usr/bin/env node // Note: // Must read https://docs.github.com/en/rest/reference/markdown#render-an-arbitrary-markdown-document // https://gist.github.com // // Good Learning sites: // https://www.w3schools.com/jquery/jquery_get_started.asp // https://www.w3schools.com/howto/howto_css_sidebar_icons.asp // // Node.js Manuals & Documentation // https://nodejs.org/docs/v0.4.0/api/all.html // * Cmd4 Body Layout * // ==================== // // ===================================== // | class="header fixed-top" // | __________________________________ // | | class="container-fluid" // | | _______________________________ // | | | class="site-logo" // | | |_______________________________ // | | | Search - tba // | | |_______________________________ // | |__________________________________ // | // |===================================== // | class="content" // | _________________________________ // | | class="sidenav" // | | - device links // | | - characteristic links // | | - cmd4Directive links // | | . // | | . // | | . // | |_________________________________ // | | class="main" // | | - class="section" - device pages // | | - class="section" - characteristic pages // | | - class="section" - directive pages // | | . // | | . // | | . // | |_________________________________ // |===================================== // | Javascript // ===================================== // * HTML Layout * // =============== // _______________________________________ // | Margin | // | _________________________________ | // | | Border | | // | | ___________________________ | | // | | | Padding | | | // | | | _____________________ | | | // | | | | | | | | // | | | | Content | | | | // | | | |_____________________| | | | // | | | | | | // | | |___________________________| | | // | | | | // | |_________________________________| | // | | // |_______________________________________| const which = require('which'); const path = require( "path" ); const settings = require( "../cmd4Settings" ); // Get the real path of homebridge instead of a dev dependancy, // which caused issues if you forget to update dependancies but // upgrade homebridge. const homebridgePath = which.sync( 'homebridge', { nothrow: true } ) let apiPath; let HomebridgeAPI; if ( homebridgePath ) { let dirname = path.dirname( homebridgePath ); console.log( "Found homebridge in path %s", dirname ); apiPath = `${ dirname }/../lib/node_modules/homebridge/lib/api`; HomebridgeAPI = require( apiPath ).HomebridgeAPI; if ( ! HomebridgeAPI ) { console.log( "homebridgeAPI not available !!!" ); process.exit( 10 ); } } else { console.log( "homebridge not found !!!" ); process.exit( 10 ); } console.log( "Found api in %s", apiPath ); // Cmd4 Constants and Settings const constants = require( "../cmd4Constants" ); //const settings = require( "../cmd4Settings" ); // For changing validValue Constants to Values and back again var { transposeValueToValidConstant } = require( "../utils/transposeCMD4Props" ); // Required to initialize CMD4 Accessory & Device lookup tables. let _api = new HomebridgeAPI( ); // object we feed to Plugins // These would be the uninitialized values. let ACC_DATA = require('../lib/CMD4_ACC_TYPE_ENUM'); let DEVICE_DATA = require('../lib/CMD4_DEVICE_TYPE_ENUM'); // Init the library for all to use let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms ); let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories ); let Characteristic = _api.hap.Characteristic; // File System utilities let fs = require( "fs" ); let CMD4_DOC_FILE_PATH = "./docs/"; let AUTO_GENERATED_SUB_PATH = "autoGenerated/"; let CMD4_ACC_DOC_FILE = "CMD4_AccessoryDescriptions.html"; // Scripts we embed let CMD4_SCRIPTS = [ { "file": "ExampleJavaScript_template.js", "filePath": "./Extras/Cmd4Scripts/Examples/", "link": "JS Template", "description": "Example Cmd4 JavaScript Template" }, { "file": "ExampleShellScript_template.sh", "filePath": "./Extras/Cmd4Scripts/Examples/", "link": "Bash Template", "description": "Example Cmd4 Shell Script Template" } ]; // The fid of the document. var fid; var divLevel=0; const INDENT = 3; // Since v6.2 the Accessory characteristics are not sorted. This // is because peoples saved status per characteristic is done by index. // changing this would mean the values would be mixed up. function sortACCByType( ) { var keysAndIndexes = []; var sorted_obj = { properties: [], indexOfEnum: CMD4_ACC_TYPE_ENUM.indexOfEnum, EOL: CMD4_ACC_TYPE_ENUM.EOL }; let characteristicNames = []; let characteristicName = ""; for ( let accTypeEnumIndex = 0 ; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex++ ) { // Set the old characteristic name and value characteristicName = Object.keys(CMD4_ACC_TYPE_ENUM).find(key => CMD4_ACC_TYPE_ENUM[key] === accTypeEnumIndex ); characteristicNames.push( characteristicName ); keysAndIndexes.push( { "key": CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].type, "index": accTypeEnumIndex } ); } // sort the keys ( types ) keysAndIndexes.sort( function(a, b) { //return a.key < b.key; if (a.key < b.key) return -1 return a.key > b.key ? 1:0 }); // create new array based on Sorted Keys for ( let index = 0 ; index < keysAndIndexes.length; index++ ) { let pair = keysAndIndexes[ index ]; let oldIndex = pair.index; sorted_obj.properties[index] = CMD4_ACC_TYPE_ENUM.properties[oldIndex]; characteristicName = pair.key; // This would be the new index sorted_obj[ characteristicName ] = index; } return sorted_obj; } // Since v6.2 the Device names are not sorted. This // is because I'm lazy to change any documentation referencing an index function sortDevByDeviceName( ) { var keysAndIndexes = []; var sorted_obj = { properties: [], indexOfEnum: CMD4_DEVICE_TYPE_ENUM.indexOfEnum, EOL: CMD4_DEVICE_TYPE_ENUM.EOL }; let deviceNames = []; let deviceName = ""; for ( let devTypeEnumIndex = 0 ; devTypeEnumIndex < CMD4_DEVICE_TYPE_ENUM.EOL; devTypeEnumIndex++ ) { // Set the old device name and value deviceName = Object.keys(CMD4_DEVICE_TYPE_ENUM).find(key => CMD4_DEVICE_TYPE_ENUM[key] === devTypeEnumIndex ); deviceNames.push( deviceName ); keysAndIndexes.push( { "key": CMD4_DEVICE_TYPE_ENUM.properties[ devTypeEnumIndex ].deviceName, "index": devTypeEnumIndex } ); } // sort the keys ( deviceName ) keysAndIndexes.sort( function(a, b) { //return a.key < b.key; if (a.key < b.key) return -1 return a.key > b.key ? 1:0 }); // create new array based on Sorted Keys for ( let index = 0 ; index < keysAndIndexes.length; index++ ) { let pair = keysAndIndexes[ index ]; let oldIndex = pair.index; sorted_obj.properties[index] = CMD4_DEVICE_TYPE_ENUM.properties[oldIndex]; deviceName = pair.key; // This would be the new index sorted_obj[ deviceName ] = index; } return sorted_obj; } let CMD4_Sorted_ACC = sortACCByType( ); let CMD4_Sorted_DEV = sortDevByDeviceName( ); function createHTMLCmd4AccDocument( ) { let file = CMD4_DOC_FILE_PATH + AUTO_GENERATED_SUB_PATH + CMD4_ACC_DOC_FILE; // Open the document for writing fid = fs.createWriteStream( file, { flags: "w" // Open for write }); fid.on("error", ( ) => { console.log(`error fs.creatingWriteStream: ${ file }` ); process.exit( 666 ); }); startHTML_AndHead( ); addSytlesAndLeftSideNav_WithinHead ( ); endHead( ); addBody( ); wfs( `\n` ); fid.close( ); } function startHTML_AndHead () { wfs( ` CMD4 Devices\n` ); } function addSytlesAndLeftSideNav_WithinHead( ) { wfs( ` \n` ); } function endHead( ) { wfs( `\n` ); } function addBody () { wfs( `\n` ); addFixedHeader_WithinBody(); addContentDiv_WithinBody( ); addAccordionScript_WithinEndOfBody(); addDropDownScript_WithinEndOfBody(); addToggleVisibilityScript_WithinEndOfBody( ); wfs( `\n` ); } function addFluidHeader_WithinFixedHeader() { wf( "+", `
\n` ); addLogoDiv_WithinFluidHeader( ); endDiv( "container-fluid" ); } function addFixedHeader_WithinBody() { wf( "+", `
\n` ); addFluidHeader_WithinFixedHeader(); wf( "-", `
\n` ); } function addContentDiv_WithinBody( ) { wf( "+", `
\n` ); addSideNav_WithinContentDiv( ) addIntroductionPage_WithinContentDiv( ) addMain_withinContentDiv( ); endDiv( "content" ); } function addMain_withinContentDiv( ) { wf( "+", `
\n` ); addDevicePages_WithinMain( ) addCharacteristicPages_WithinMain( ); addCmd4DirectivePages_WithinMain( ); addCmd4AccessoryDirectivePages_WithinMain( ); addCustomCharacteristicPages_withinMain( ); addScriptTemplatePages_WithinMain( ); endDiv( "main" ); } function addLogoDiv_WithinFluidHeader( ) { wf( "+", `