├── test
├── AdvAir.sh
├── echoScripts
│ ├── justExitWithRCof0
│ ├── justExitWithRCof1
│ ├── echo_0
│ ├── echo_1
│ ├── echo_On
│ ├── runToTimeoutRcOf0
│ ├── runToTimeoutRcOf1
│ ├── echo_false
│ ├── echo_nothing
│ ├── echo_null
│ ├── echo_true
│ ├── echo_ENABLED
│ ├── echo_INACTIVE
│ ├── echo_quoted0
│ ├── echo_quoted1
│ ├── echo_DISABLED
│ ├── echo_quotedFALSE
│ ├── echo_quotedNULL
│ ├── echo_quotedNothing
│ ├── echo_quotedTRUE
│ ├── echo_true_withRcOf1
│ ├── echo_after5seconds
│ ├── echo_ACTIVE
│ ├── echo_errorToStderr
│ ├── echo_nullAndErrorToStderr
│ ├── echo_too_much
│ └── testGetSetValues.js
├── sanityTests
├── testOurConfig.json.js
├── allTests
├── async-dump.js
├── getAccessoryUUID.js
├── extractKeyValue.js
├── isCmd4Directive.js
├── versionChecker.js
├── isAccDirective.js
├── isJSON.js
├── isDevDirective.js
├── VariableTimer.js
├── loadPluginTest.js
├── isNumeric.js
├── indexOfEnum.js
├── mocha-setup
├── trueTypeOf.js
├── Cmd4Mode.js
├── CMD4_CHAR_TYPE_ENUMS.js
├── HV.js
├── getAccessoryNameFunctions.js
├── initPluginTest.js
├── getSetAllValues.js
└── Logger.js
├── screenshots
├── Eve_screenshot.png
└── Homekit_screenshot.png
├── Extras
└── Cmd4Scripts
│ ├── Examples
│ ├── middleWare.sh
│ ├── DoorLock.sh
│ ├── basic_ping.sh
│ ├── advanced_ping.sh
│ ├── PS5.sh
│ ├── ExampleShellScript_template.sh
│ ├── PS4.sh
│ ├── wakeonlan.sh
│ └── ExampleJavaScript_template.js
│ └── CheckYourScript.sh
├── .npmignore
├── CHANGELOG.md
├── docs
├── index.html
└── AdvancedTroubleShooting.md
├── utils
├── isNumeric.js
├── extractKeyValue.js
├── indexOfEnum.js
├── lcFirst.js
├── ucFirst.js
├── isJSON.js
├── getAccessoryUUID.js
├── getAccessoryNameFunctions.js
├── trueTypeOf.js
├── isCmd4Directive.js
├── isAccDirective.js
├── createAccessorysInformationService.js
├── isDevDirective.js
├── indexOfEnumLintTest.js
├── VariableTimer.js
├── HV.js
├── versionChecker.js
├── transposeCMD4Props.js
├── Logger.js
└── Cmd4Storage.js
├── .gitignore
├── cmd4Settings.js
├── commitlint.config.js
├── .github
├── pull_request_template.md
└── ISSUE_TEMPLATE
│ ├── feature-request.md
│ ├── bug-report.md
│ └── support-request.md
├── .eslintrc.json
├── postinstall.js
├── index.js
├── lib
└── CMD4_CHAR_TYPE_ENUMS.js
├── tools
└── whereIsConstant
├── package.json
└── cmd4Constants.js
/test/AdvAir.sh:
--------------------------------------------------------------------------------
1 | ../Extras/Cmd4Scripts/Examples/AnyDevice
--------------------------------------------------------------------------------
/test/echoScripts/justExitWithRCof0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | exit 0;
4 |
--------------------------------------------------------------------------------
/test/echoScripts/justExitWithRCof1:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | exit 1;
4 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo 0;
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_1:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo "1";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_On:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo On;
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/runToTimeoutRcOf0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1000000;
4 | exit 0;
5 |
--------------------------------------------------------------------------------
/test/echoScripts/runToTimeoutRcOf1:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 1000000;
4 | exit 1;
5 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_false:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo false;
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_nothing:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo "";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_null:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo "null";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_true:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo true;
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_ENABLED:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo "Enabled";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_INACTIVE:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo INACTIVE;
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_quoted0:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo \"0\";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_quoted1:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo \"1\";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_DISABLED:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo "DISABLED";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_quotedFALSE:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo \"False\";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_quotedNULL:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo \"NULL\";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_quotedNothing:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo \" \";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_quotedTRUE:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo \"True\";
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_true_withRcOf1:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo true;
4 |
5 | exit 1;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_after5seconds:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 5;
4 | stdbuf -o0 echo false;
5 |
6 | exit 0;
7 |
--------------------------------------------------------------------------------
/screenshots/Eve_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ztalbot2000/homebridge-cmd4/HEAD/screenshots/Eve_screenshot.png
--------------------------------------------------------------------------------
/test/echoScripts/echo_ACTIVE:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo "blast" > /tmp/blast;
3 |
4 | stdbuf -o0 echo ACTIVE;
5 |
6 | exit 0;
7 |
--------------------------------------------------------------------------------
/screenshots/Homekit_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ztalbot2000/homebridge-cmd4/HEAD/screenshots/Homekit_screenshot.png
--------------------------------------------------------------------------------
/test/echoScripts/echo_errorToStderr:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -e0 -i0 echo "This message goes to stderr" >&2;
4 |
5 | exit 0;
6 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_nullAndErrorToStderr:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo null
4 | stdbuf -o0 e0 echo "This message goes to stderr" >&2;
5 |
6 | exit 0;
7 |
--------------------------------------------------------------------------------
/test/echoScripts/echo_too_much:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | stdbuf -o0 echo "";
4 | stdbuf -o0 echo "second extra line";
5 | stdbuf -o0 echo "third extra line";
6 |
7 | exit 0;
8 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/middleWare.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | result=$(node .homebridge/Cmd4Scripts/State.js $* 2>&1)
3 | echo $( date ) >> /tmp/Cmd4.log
4 | echo $* >> /tmp/Cmd4.log
5 | echo $result | tee -a /tmp/Cmd4.log
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .eslintrc.json
2 | .gitignore
3 | .github
4 | .huskyrc
5 | .DS_Store
6 | *_save*
7 | *_bak*
8 | *.swp
9 | commitlint.config.js
10 | jsmin
11 | utils/indexOfEnumLintTest.js
12 | screenshots
13 | test
14 | node_modules
15 | local
16 | tools
17 | docs/autoGenerated
18 | docs/images
19 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Homebridges-cmd4 - CMD4 Plugin for Homebridge - Supports ~All Accessory Types and now all Characteristics too!!
2 | #### 8.0.3 (2024-12-11)
3 |
4 | ##### Bug Fixes
5 |
6 | * Update node requirements to satisfy the current Node.js version of v22.12.0 ([22761c89](https://github.com/ztalbot2000/homebridge-cmd4/commit/22761c8954dfdb6086023585978836b36f531f65))
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Please follow this link.
8 |
9 |
10 |
--------------------------------------------------------------------------------
/utils/isNumeric.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Determine if a given parameter is numeric.
5 | //
6 | // @param num - The number to determine if it is actually numeric.
7 | // @returns: boolean
8 | //
9 |
10 | var isNumeric = function( num )
11 | {
12 | num = "" + num; // coerce num to be a string
13 | return !isNaN( num ) && !isNaN( parseFloat( num ) );
14 | }
15 |
16 | module.exports = isNumeric;
17 |
--------------------------------------------------------------------------------
/utils/extractKeyValue.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Extracts a key from an object given a value.
5 | //
6 | // @param obj - The object to get the key from.
7 | // @param value - The value to find.
8 | //
9 | // @returns key or undefined
10 | //
11 |
12 | var extractKeyValue = function (obj, value) {
13 | return Object.keys(obj)[Object.values(obj).indexOf(value)];
14 | }
15 |
16 | module.exports = extractKeyValue;
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Mac
2 | .DS_Store
3 |
4 | # Text editors
5 | *.swp
6 |
7 | # Stupid commits of npm package
8 | *.tgz
9 |
10 | # My favorite temp backup
11 | *.[0-9]
12 | *.save*
13 | *_save*
14 | *_bak*
15 |
16 | # Compiled Executables
17 | jsmin
18 | a.out
19 |
20 | # node
21 | node_modules
22 | npm-debug.log
23 | .node-version
24 |
25 | test/tmp/*
26 |
27 | # Not a fan
28 | package-lock.json
29 |
30 | # work in progress folder
31 | local
32 |
--------------------------------------------------------------------------------
/test/sanityTests:
--------------------------------------------------------------------------------
1 | test/Cmd4Storage.js
2 | test/HV.js
3 | test/cmd4Constants.js
4 | test/CMD4_CHAR_TYPE_ENUMS.js
5 | test/CMD4_ACC_TYPE_ENUM.js
6 | test/CMD4_DEVICE_TYPE_ENUM.js
7 | test/isAccDirective.js
8 | test/isDevDirective.js
9 | test/isCmd4Directive.js
10 | test/configTest.js
11 | test/loadPluginTest.js
12 | test/Cmd4Accessory.js
13 | test/internalRelatedTargetTests.js
14 | test/Cmd4Platform.js
15 | test/Cmd4AccessoryGetValue.js
16 | test/Cmd4AccessorySetValue.js
17 | test/Cmd4Mode.js
18 | test/Cmd4PriorityPollingQueue.js
19 | test/initPluginTest.js
20 | test/fakeGato.js
21 | test/pollingTest.js
22 | test/Cmd4PlatformRestartTests.js
23 |
--------------------------------------------------------------------------------
/test/testOurConfig.json.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs'),
4 | Parser = require('jsonparse');
5 |
6 |
7 | var json = fs.readFileSync("Extras/config.json");
8 |
9 |
10 |
11 | describe( "Testing our config.json", ( ) =>
12 | {
13 | it( "isJSON should be a function", ( ) =>
14 | {
15 | assert.isFunction( Parser, "Parser is not a function" );
16 | });
17 |
18 | it( "If our config.json is valid", ( ) =>
19 | {
20 | JSON.parse(json);
21 | var p = new Parser();
22 |
23 | p.onError = function (value) {
24 | assert("Our config.json is invalid value:", value);
25 | };
26 |
27 | p.write( json );
28 |
29 | });
30 | })
31 |
--------------------------------------------------------------------------------
/utils/indexOfEnum.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | // Description:
5 | // Create an Object prototype to for getting an index of an enumerated type.
6 | //
7 |
8 | module.exports=Object.defineProperty(Object.prototype, "indexOfEnum", {
9 | value: function( predicate, fromIndex ) {
10 | let length = this == null ? 0 : Object.keys( this ).length;
11 | if ( !length )
12 | return -1;
13 |
14 | let index = fromIndex == null ? 0 : fromIndex;
15 | if ( index < 0 )
16 | {
17 | index = Math.max( length + index, 0 );
18 | }
19 |
20 | for ( let i=index; i < length; i++)
21 | {
22 | if ( predicate( this[ i ], i, this ) )
23 | {
24 | return i;
25 | }
26 | }
27 | return -1;
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/cmd4Settings.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | //
3 | // This is the name of the platform that users will use to register
4 | // the plugin in the Homebridge config.json
5 | //
6 | exports.PLATFORM_NAME = "Cmd4";
7 |
8 | //
9 | // This *MUST* match the name of your plugin as defined the package.json
10 | //
11 | exports.PLUGIN_NAME = "homebridge-cmd4";
12 |
13 | // These must be global so that all characteristics are not
14 | // polled at the same time. Specifically a MyAir that has
15 | // multiple fans, switches and temperature sensors, all in
16 | // the same device of which a linkedAccessory is not an option.
17 | //exports.arrayOfAllStaggeredPollingCharacteristics = [ ];
18 | exports.listOfCreatedPriorityQueues = { };
19 |
20 |
21 | // By using our own Logger, we don't trigger others
22 | exports.cmd4Dbg = false;
23 |
--------------------------------------------------------------------------------
/utils/lcFirst.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Convert the first character of a string to lower case.
5 | //
6 | // @param string - The string to convert.
7 | //
8 | // @returns: The string with the first letter lower cased.
9 | //
10 | //
11 | var lcFirst = function( string )
12 | {
13 | switch( typeof string )
14 | {
15 | case undefined:
16 |
17 | console.log( "Asked to lower case first character of NULL String" );
18 | return undefined;
19 |
20 | case "boolean":
21 | case "number":
22 | return string;
23 | case "string":
24 | return string.charAt( 0 ).toLowerCase() + string.slice( 1 );
25 | default:
26 | console.log( "Asked to lower case first character of non String(%s):%s", typeof string, string );
27 | return string;
28 | }
29 | }
30 |
31 | module.exports = lcFirst;
32 |
--------------------------------------------------------------------------------
/utils/ucFirst.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Convert the first character of a string to upper case.
5 | //
6 | // @param string - The string to convert.
7 | //
8 | // @returns: The string with the first letter upper cased.
9 | //
10 | //
11 | var ucFirst = function( string )
12 | {
13 | switch( typeof string )
14 | {
15 | case undefined:
16 |
17 | console.log( "Asked to upper case first character of NULL String" );
18 | return undefined;
19 |
20 | case "boolean":
21 | case "number":
22 | return string;
23 | case "string":
24 | return string.charAt( 0 ).toUpperCase() + string.slice( 1 );
25 | default:
26 | console.log( "Asked to upper case first character of non String(%s):%s", typeof string, string );
27 | return string;
28 | }
29 | }
30 |
31 | module.exports = ucFirst;
32 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'] ,
3 | plugins: ['commitlint-plugin-function-rules'],
4 | rules: {
5 | 'header-case': [ 0 ],
6 | 'body-max-line-length': [0], // level: disabled
7 | 'function-rules/header-case': [
8 | 2, // level: error
9 | 'always',
10 | () => {
11 | // I do not care about case in header
12 | return [true];
13 | }
14 | ],
15 | 'body-case': [ 0 ],
16 | 'function-rules/body-case': [
17 | 2, // level: error
18 | 'always',
19 | () => {
20 | // I do not care about case in body
21 | return [true];
22 | }
23 | ],
24 | 'subject-case': [ 0 ],
25 | 'function-rules/subject-case': [
26 | 2, // level: error
27 | 'always',
28 | () => {
29 | // I do not care about case in subject
30 | return [true];
31 | }
32 | ]
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/test/allTests:
--------------------------------------------------------------------------------
1 | test/Logger.js
2 | test/async-dump.js
3 | test/configHasCharacteristicProps.js
4 | test/isJSON.js
5 | test/indexOfEnum.js
6 | test/isNumeric.js
7 | test/extractKeyValue.js
8 | test/getAccessoryNameFunctions.js
9 | test/getAccessoryUUID.js
10 | test/getSetAllValues.js
11 | test/VariableTimer.js
12 | test/transposeCMD4Props.js
13 | test/trueTypeOf.js
14 | test/versionChecker.js
15 | test/Cmd4Storage.js
16 | test/HV.js
17 | test/cmd4Constants.js
18 | test/CMD4_CHAR_TYPE_ENUMS.js
19 | test/CMD4_ACC_TYPE_ENUM.js
20 | test/CMD4_DEVICE_TYPE_ENUM.js
21 | test/isAccDirective.js
22 | test/isDevDirective.js
23 | test/isCmd4Directive.js
24 | test/testOurConfig.json.js
25 | test/configTest.js
26 | test/loadPluginTest.js
27 | test/Cmd4Accessory.js
28 | test/internalRelatedTargetTests.js
29 | test/Cmd4Platform.js
30 | test/Cmd4AccessoryGetValue.js
31 | test/Cmd4AccessorySetValue.js
32 | test/Cmd4Mode.js
33 | test/Cmd4PriorityPollingQueue.js
34 | test/initPluginTest.js
35 | test/fakeGato.js
36 | test/pollingTest.js
37 | test/Cmd4PlatformRestartTests.js
38 |
--------------------------------------------------------------------------------
/utils/isJSON.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Determine if parameter is a true JSON object, not an array, but {}
5 | //
6 | // @param m - JSON Object to check.
7 | // @returns: boolean
8 |
9 | function isJSON( m )
10 | {
11 | if ( ! m )
12 | {
13 | console.warn( "No parameter passed to isJSON" );
14 | return false;
15 | }
16 |
17 | if ( ! m.constructor )
18 | {
19 | //console.warn( "No constructor to isJSON for parameter: %s", m );
20 | return false;
21 | }
22 |
23 | if ( m.constructor === Array )
24 | {
25 | //console.warn( "It is an array" );
26 | return false;
27 | }
28 |
29 | if ( typeof m == "object" )
30 | {
31 | try{ m = JSON.stringify( m ); }
32 | catch( err ) { return false; } }
33 |
34 | if ( typeof m == "string")
35 | {
36 | try{ m = JSON.parse( m ); }
37 | catch ( err ) { return false; } }
38 |
39 | if ( typeof m != "object") { return false; }
40 |
41 | return true;
42 | }
43 |
44 |
45 | module.exports = isJSON;
46 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Pull Request
3 | about: Resolve an issue or add an improvement to cmd4.
4 | title: "[Pull Request]"
5 | labels: pull-request
6 | assignees: ztalbot2000
7 |
8 | ---
9 |
10 |
11 |
12 | **Is your pull request related to a problem? Please describe:**
13 |
14 |
15 | **Describe the solution you'd have implemented:**
16 |
17 |
18 | **Do your changes pass unit testing ( npm run test ) :**
19 | - [x] Yes
20 | - [ ] No
21 |
22 | **Do your changes pass lint testing ( npm run lint ) :**
23 | - [x] Yes
24 | - [ ] No
25 |
26 | **Additional context:**
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/utils/getAccessoryUUID.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Get or create a Accessories UUID based on what it is configured as.
5 | //
6 | // @param config - The accessories config information.
7 | // @param UUIDGen - api.hap.uuid
8 | //
9 | // @returns - UUID or exits as all Accessories must have a name or displayName.
10 | //
11 | // Note: This follows the getAccessoryName logic of getting the Accessories name.
12 |
13 | var getAccessoryUUID = function ( config, UUIDGen )
14 | {
15 | if ( config.UUID )
16 | return config.UUID;
17 | if ( config.uuid )
18 | return config.uuid;
19 |
20 | if ( config.name )
21 | return UUIDGen.generate( config.name );
22 |
23 | if ( config.Name )
24 | return UUIDGen.generate( config.Name );
25 |
26 | if ( config.displayName )
27 | return UUIDGen.generate( config.displayName );
28 |
29 | if ( config.DisplayName )
30 | return UUIDGen.generate( config.DisplayName );
31 |
32 | throw new Error( "You must either, 'displayName' and or 'name' per accessory." );
33 | }
34 |
35 | module.exports = getAccessoryUUID;
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea or improvement for cmd4.
4 | title: "[Feature Request]"
5 | labels: enhancement
6 | assignees: ztalbot2000
7 |
8 | ---
9 | ** Cmd4 No longer supported:**
10 |
11 |
12 |
13 |
14 | **Is your feature request related to a problem? Please describe:**
15 |
16 |
17 | **Describe the solution you'd like:**
18 |
19 |
20 | **Describe alternatives you've considered:**
21 |
22 |
23 | **Additional context:**
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/utils/getAccessoryNameFunctions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Routines of which given an Accessory Config. Extract the Accessory Name based on
5 | // either it's displayName or name key values.
6 | //
7 | // @param config - The Accessories json config.
8 | //
9 | // @returns name as a string or dies as all accessories must have a name
10 | //
11 |
12 | var getAccessoryName = function ( config )
13 | {
14 | if ( config.name ) return config.name;
15 | if ( config.Name ) return config.Name;
16 | if ( config.displayName ) return config.displayName;
17 | if ( config.DisplayName ) return config.DisplayName;
18 |
19 | throw new Error( "You must have a 'Name' per accessory." );
20 | }
21 |
22 | var getAccessoryDisplayName = function ( config )
23 | {
24 | if ( config.displayName ) return config.displayName;
25 | if ( config.DisplayName ) return config.DisplayName;
26 |
27 | if ( config.name ) return config.name;
28 | if ( config.Name ) return config.Name;
29 |
30 | throw new Error( "You must either, 'displayName' and or 'name' per accessory." );
31 | }
32 |
33 | module.exports = { getAccessoryName,
34 | getAccessoryDisplayName
35 | };
36 |
--------------------------------------------------------------------------------
/utils/trueTypeOf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Determine the true type of an object, because typeOf is screwy
5 | // for null/undefined.
6 | //
7 | //
8 | // NOTE: "0" or any qoted number is still a string.
9 | // This function just fixes null/undefined.
10 | // Use isNumeric if needed.
11 | //
12 | // @param m - type to check
13 | // @returns: Array, Boolean, Number, String, Object, null, undefined
14 |
15 | function trueTypeOf( m )
16 | {
17 | switch( typeof m )
18 | {
19 | case "boolean":
20 | return Boolean;
21 | case "number":
22 | return Number;
23 | case "string":
24 | // If the string is actually a number, let the caller
25 | // deal with it as our intent is just to fix undefined
26 | // and null issues.
27 | return String;
28 | case "object":
29 | // null can be an object
30 | if ( m == null )
31 | return null;
32 | if ( Array.isArray( m ) )
33 | return Array;
34 |
35 | return Object;
36 | case "undefined":
37 | return undefined;
38 | default:
39 | throw new Error("OOPS");
40 | }
41 |
42 | }
43 |
44 | module.exports = trueTypeOf;
45 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": false,
4 | "commonjs": false,
5 | "node": true,
6 | "mocha": true,
7 | "es2021": true
8 | },
9 | "globals": {
10 | "assert" : "writeable",
11 | "expect" : "writeable",
12 | "sinon" : "writeable",
13 | "ACC_EOL" : "readonly",
14 | "DEVICE_EOL" : "readonly",
15 | "FORMAT_EOL" : "readonly",
16 | "UNITS_EOL" : "readonly",
17 | "PERMS_EOL" : "readonly",
18 | "DEVICE_DATA" : "readonly",
19 | "ACC_DATA" : "readonly",
20 | "CHAR_DATA" : "readonly",
21 | "CMD4_CHAR_TYPE_ENUMS" : "readonly",
22 | "CMD4_ACC_TYPE_ENUM" : "readonly",
23 | "CMD4_DEVICE_TYPE_ENUM" : "readonly",
24 | "cleanStatesDir" : "readonly",
25 | "accEnumIndexToC" : "readonly",
26 | "devEnumIndexToC" : "readonly",
27 | "fs" : "writeable",
28 | "HomebridgeAPI" : "writeable",
29 | "Logger" : "writeable",
30 | "platformAccessory_1" : "writeable"
31 | },
32 | "extends": "eslint:recommended",
33 | "parserOptions": {
34 | "ecmaVersion": 12
35 | },
36 | "rules": {
37 | "no-fallthrough": ["error", { "commentPattern": "break[\\s\\w]*omitted"}],
38 | "no-whitespace-before-property": ["error"],
39 | "arrow-spacing": ["error"]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/test/async-dump.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // How To use:
4 | //
5 | // 1. In package.json change scipt "test", adding:
6 | // node_modules/.bin/mocha --require ./test/async-dump.js
7 | // 2. In the failing test add at the top:
8 | // after(function () {
9 | // global.asyncDump();
10 | // });
11 | // 3. Run the test
12 | // npm run test test/failingTest
13 | //
14 | //
15 | // Taken from: https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843
16 |
17 | const { createHook } = require( 'async_hooks' );
18 | const { stackTraceFilter } = require( 'mocha/lib/utils' );
19 | const allResources = new Map();
20 |
21 | // this will pull Mocha internals out of the stacks
22 | const filterStack = stackTraceFilter();
23 |
24 | const hook = createHook({
25 | init(asyncId, type, triggerAsyncId)
26 | {
27 | allResources.set(asyncId, { type, triggerAsyncId, stack: ( new Error( ) ).stack });
28 | },
29 | destroy( asyncId ) {
30 | allResources.delete( asyncId );
31 | }
32 | }).enable( );
33 |
34 | global.asyncDump = module.exports = ( ) => {
35 | hook.disable( );
36 | console.error(`
37 | STUFF STILL IN THE EVENT LOOP:`)
38 | allResources.forEach(value => {
39 | console.error( `Type: ${value.type}` );
40 | console.error( filterStack(value.stack ) );
41 | console.error( '\n' );
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/utils/isCmd4Directive.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const constants = require( "../cmd4Constants" );
4 | const lcFirst = require( "./lcFirst" );
5 |
6 |
7 | // Description:
8 | // Determine if parameter is a Cmd4 directive based on it being
9 | // in the cmd4Constants.
10 | //
11 | // @param directive - The Cmd4 Directive to check
12 | // @param allowUpper - if upper case allowed to be checked.
13 | // @returns: { key: The CORRECT directive
14 | // wasLower: If the directive was lowered to make it correct.
15 | // } or null
16 |
17 |
18 | function isCmd4Directive( directive, allowUpper = false )
19 | {
20 | if ( ! directive )
21 | {
22 | console.warn( "No parameter passed to isCmd4Directive" );
23 | return null;
24 | }
25 |
26 | if ( Object.values( constants ).indexOf( directive ) >= 0 )
27 | {
28 | //console.log("isCmd4Directive directive: %s returning: %s", directive, directive );
29 | return { key: directive,
30 | wasLower: true };
31 | }
32 |
33 | if ( directive == "UUID" )
34 | return { key: "uuid",
35 | wasLower: false };
36 |
37 | // Note: There are othes like WiFi ... but nobody uses them thankfully !
38 | if ( allowUpper == true )
39 | {
40 |
41 | let lcDirective = lcFirst( directive );
42 | if ( Object.values( constants ).indexOf( lcDirective ) >= 0 )
43 | {
44 | //console.log("isCmd4Directive directive: %s returning: %s", directive, lcDirective );
45 | return { key: lcDirective,
46 | wasLower: false };
47 | }
48 | }
49 |
50 | //console.log("isCmd4Directive directive: %s returning null", directive );
51 |
52 | return null;
53 | }
54 |
55 |
56 | module.exports = isCmd4Directive;
57 |
--------------------------------------------------------------------------------
/test/getAccessoryUUID.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _api = new HomebridgeAPI(); // object we feed to Plugins
4 | var pluginModule = require( "../index" );
5 |
6 | var getAccessoryUUID = require( "../utils/getAccessoryUUID.js" );
7 |
8 | describe("Quick Testing load of index.js", ( ) =>
9 | {
10 | it("API should not be null", ( ) =>
11 | {
12 | assert.isNotNull(_api, "_api is null" );
13 | });
14 |
15 | it("index.js loaded should not be null", ( ) =>
16 | {
17 | assert.isNotNull(pluginModule, "loading resulted in null" );
18 | });
19 |
20 | it("UUIDGen should be found", ( ) =>
21 | {
22 | var t = typeof _api.hap.uuid.generate;
23 | assert.equal(t, "function" );
24 | });
25 | });
26 |
27 | describe( "Testing getAccessoryUUID.js", ( ) =>
28 | {
29 | it( "getAccessoryUUID.js should be a function", ( ) =>
30 | {
31 |
32 | assert.isFunction( getAccessoryUUID, "getAccessoryUUID is not a function" );
33 | });
34 |
35 | it( "getAccessoryUUID should return a string ", ( ) =>
36 | {
37 | let config = {displayName: "Kodi",
38 | name: "blah"
39 | };
40 | let result = getAccessoryUUID( config, _api.hap.uuid);
41 |
42 | assert.isString( result, "getAccessoryUUID should return a string. result:: " + result );
43 | });
44 |
45 | it( "getAccessoryUUID should return a string length 36 ", ( ) =>
46 | {
47 | let config = { name: "Kodi",
48 | configuredUUID: "blah"
49 | };
50 | let result = getAccessoryUUID( config, _api.hap.uuid);
51 |
52 | assert.isString( result, "getAccessoryUUID should return a string. result:: " + result );
53 |
54 | assert.equal( result.length, 36, "getAccessoryUUID return a string length of 36. result:: " + result.length );
55 | });
56 | })
57 |
--------------------------------------------------------------------------------
/utils/isAccDirective.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let CMD4_ACC_TYPE_ENUM = require( "../lib/CMD4_ACC_TYPE_ENUM" ).CMD4_ACC_TYPE_ENUM;
4 |
5 |
6 | // Description:
7 | // Determine if parameter is a Cmd4 accessory characteristic
8 | //
9 | // @param type - The characteristic type to check. i.e. "On"
10 | // @param allowUpper - if upper case allowed to be checked.
11 | // @returns: { type: The CORRECT characteristic type
12 | // accTypeEnumIndex: The index of the characteristic
13 | // } or null
14 | //
15 |
16 | function isAccDirective( type, allowUpper = false )
17 | {
18 | // For backward compatability of testStoredValueForIndex of FakeGato
19 | // we must return a null accTypeIndex, which should be checked instead
20 | // of just rc.
21 | let defaultRc = { "type": type,
22 | "accTypeEnumIndex": null
23 | };
24 |
25 | if ( ! type )
26 | {
27 | console.warn( "No parameter passed to isCmd4Directive" );
28 | return defaultRc;
29 | }
30 |
31 | let accTypeEnumIndex;
32 |
33 | // We want lower case to be correct
34 | accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.sche === type )
35 | if ( accTypeEnumIndex >= 0 )
36 | return { "type": type,
37 | "accTypeEnumIndex": accTypeEnumIndex };
38 |
39 | // Note: There are othes like WiFi ... but nobody uses them thankfully !
40 | if ( allowUpper == true )
41 | {
42 | accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === type );
43 |
44 | // We return the correct lower case
45 | if ( accTypeEnumIndex >= 0 )
46 | return { "type": CMD4_ACC_TYPE_ENUM.properties[ accTypeEnumIndex ].sche,
47 | "accTypeEnumIndex": accTypeEnumIndex };
48 | }
49 |
50 | return defaultRc;
51 | }
52 |
53 |
54 | module.exports = isAccDirective;
55 |
--------------------------------------------------------------------------------
/utils/createAccessorysInformationService.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function createAccessorysInformationService( accessory )
4 | {
5 | // Standalone accessories do not have platforms
6 | if ( accessory.platform )
7 | {
8 | // Platform accessories may already have information sercices if
9 | // they are restored from cache by Homebridge.
10 | let informationService = accessory.platform.getService( accessory.api.hap.Service.AccessoryInformation )
11 | if ( informationService )
12 | {
13 | accessory.log.debug( `Using Existing ( cached ) accessory information service for: ${ accessory.displayName }` );
14 | accessory.informationService = informationService;
15 | }
16 | }
17 |
18 | if ( ! accessory.informationService )
19 | {
20 | accessory.log.debug( `Creating new accessory information service for: ${ accessory.displayName }` );
21 |
22 | // Create accessory's Information Service
23 | accessory.informationService = new accessory.api.hap.Service.AccessoryInformation( );
24 | }
25 |
26 |
27 | // Add/update the Model characteristic, if it is defined.
28 | if ( accessory.model )
29 | accessory.informationService
30 | .setCharacteristic( accessory.api.hap.Characteristic.Model, accessory.model );
31 |
32 | // Add/update the Manufacturer characteristic, if it is defined.
33 | if ( accessory.manufacturer )
34 | accessory.informationService
35 | .setCharacteristic( accessory.api.hap.Characteristic.Manufacturer, accessory.manufacturer );
36 |
37 | // Add/update the serialNumber characteristic, if it is defined.
38 | if ( accessory.serialNumber )
39 | accessory.informationService
40 | .setCharacteristic( accessory.api.hap.Characteristic.SerialNumber, accessory.serialNumber );
41 |
42 | if ( accessory.services )
43 | accessory.services.push( accessory.informationService );
44 | }
45 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Raise a bug related report for cmd4.
4 | title: "[Bug]"
5 | labels: bug
6 | assignees: ztalbot2000
7 |
8 | ---
9 | ** Cmd4 No longer supported:**
10 |
11 |
12 |
13 |
14 | **Describe The Bug:**
15 |
16 |
17 | **To Reproduce:**
18 |
19 |
20 | **Expected Behaviour:**
21 |
22 |
23 | [**Link to Logs:**]()
24 |
25 |
26 |
27 |
28 |
29 | **Paste of Logs:**
30 | ```
31 |
32 | ```
33 |
34 | **Cmd4 Config:**
35 |
36 |
37 |
38 | ```json
39 |
40 |
41 | ```
42 |
43 | **Screenshots:**
44 |
45 |
46 | **Environment:**
47 |
48 | * **Node.js Version**:
49 | * **NPM Version**:
50 | * **Homebridge Version**:
51 | * **homebridge-cmd4 Version**:
52 | * **Operating System**: Raspbian / Ubuntu / Debian / Windows / macOS / Docker / other
53 | * **Process Supervisor**: Systemd / init.d / pm2 / launchctl / Docker / hb-service / other / none
54 |
55 |
56 |
--------------------------------------------------------------------------------
/utils/isDevDirective.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let ucFirst = require( "../utils/ucFirst" );
4 |
5 |
6 | let CMD4_DEVICE_TYPE_ENUM = require( "../lib/CMD4_DEVICE_TYPE_ENUM" ).CMD4_DEVICE_TYPE_ENUM;
7 |
8 |
9 | // Description:
10 | // Determine if type is a Cmd4 device based CMD4_DEVICE_TYPE_ENUM.deviceName
11 | //
12 | // @param deviceName - The device name to check. i.e. "Switch"
13 | // @param allowUpper - if upper case allowed to be checked.
14 | // @returns: { deviceName: The CORRECT device name
15 | // devEnumIndex: The index of the characteristic
16 | // } or null
17 |
18 |
19 | function isDevDirective( deviceName, allowUpper = false )
20 | {
21 | // We return similiar to isAccDirective
22 | // We must return a null devTypeIndex, which should be checked instead
23 | // of just rc, like isAccDirective
24 | let defaultRc = { "deviceName": deviceName,
25 | "devEnumIndex": null
26 | };
27 |
28 | if ( ! deviceName )
29 | {
30 | console.warn( "No parameter passed to isDevDirective" );
31 | return defaultRc;
32 | }
33 |
34 | let devEnumIndex;
35 |
36 | // We want lower case to be correct
37 | devEnumIndex = CMD4_DEVICE_TYPE_ENUM.indexOfEnum( deviceName );
38 | if ( devEnumIndex >= 0 )
39 | return { "deviceName": deviceName,
40 | "devEnumIndex": devEnumIndex };
41 |
42 | // Note: There are othes like WiFi ... but nobody uses them thankfully !
43 | if ( allowUpper == true )
44 | {
45 | let ucDeviceName = ucFirst( deviceName );
46 | devEnumIndex = CMD4_DEVICE_TYPE_ENUM.indexOfEnum( ucDeviceName );
47 |
48 | // We return the correct upper case
49 | if ( devEnumIndex >= 0 )
50 | return { "deviceName": CMD4_DEVICE_TYPE_ENUM.devEnumIndexToC( devEnumIndex ),
51 | "devEnumIndex": devEnumIndex };
52 | }
53 |
54 | return defaultRc;
55 | }
56 |
57 |
58 | module.exports = isDevDirective;
59 |
--------------------------------------------------------------------------------
/postinstall.js:
--------------------------------------------------------------------------------
1 | // Post install notes
2 |
3 |
4 | // Fun colour stuff
5 | const chalk = require( "chalk" );
6 |
7 | const myPkg = require( "./package.json" );
8 |
9 | const { isUpgrade } = require( "./utils/versionChecker" );
10 |
11 | // To use await you must be in an async function, so put it in one.
12 | ( async( ) =>
13 | {
14 | // Wait for the Promise of isUpgrade to complete.
15 | let lv = await isUpgrade( );
16 |
17 | if ( lv == true )
18 | {
19 | console.log( chalk.green( `[UPDATE AVAILABLE] ` ) + `Version ${lv} of ${myPkg.name} is available. Any release notes can be found here: ` + chalk.underline( `${myPkg.changelog}` ) );
20 | }
21 | console.log( chalk.yellow( `HomeBridge-Cmd4 5.0.0+ Important Notice:\n` ) );
22 | 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` );
23 | 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.` );
24 | console.log( chalk.red( `* ` ) + `RestartRecovery is now automatic; which not enabling could cause your device to turn on/off over a restart.` );
25 | 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.` );
26 | console.log( chalk.red( `* ` ) + `Demo mode is still available by not defining any polling.\n` );
27 |
28 | console.log( chalk.underline( `Cmd4 New Users` ) );
29 | console.log( chalk.green( `* ` ) + `You will need to follow the README to continue the configuration of HomeBridge-CMD4.\n` );
30 |
31 | console.log(`\n As always, if you like this plugin, don't forget to star it on GitHub.\n`);
32 | console.log(`\n Enjoy`);
33 | console.log(` John Talbot\n`);
34 | })( );
35 |
36 |
--------------------------------------------------------------------------------
/test/extractKeyValue.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var CMD4_ACC_TYPE_ENUM = {
4 | AccessoryFlags: 0,
5 | properties: {}
6 | };
7 | CMD4_ACC_TYPE_ENUM.properties =
8 | {
9 | 0: { type: "AccessoryFlags",
10 | // characteristic: Characteristic.AccessoryFlags,
11 | // props: {format: Characteristic.Formats.UINT32,
12 | // perms: [Characteristic.Perms.READ,
13 | // Characteristic.Perms.NOTIFY
14 | // ]
15 | // },
16 | validValues:
17 | {"OPEN": 0,
18 | "CLOSED": 1,
19 | "OPENING": 2,
20 | "CLOSING": 3,
21 | "STOPPED": 4
22 | }
23 | }
24 | };
25 |
26 | var extractKeyValue = require( "../utils/extractKeyValue.js" );
27 |
28 | describe( "Testing extractKeyValue", ( ) =>
29 | {
30 | it( "extractKeyValue should be a function", ( ) =>
31 | {
32 | assert.isFunction( extractKeyValue, "extractKeyValue is not a function" );
33 | });
34 |
35 | it( "test 1 extractKeyValue should return correct key", ( ) =>
36 | {
37 | let expectedKey = "CLOSING";
38 | let value = 3;
39 | let result = extractKeyValue( CMD4_ACC_TYPE_ENUM.properties[0].validValues, value );
40 | assert.equal( result, expectedKey, "Test 1 extractKeyValue( " + value + " ) returned:" + result + " expected:" + expectedKey );
41 | });
42 |
43 | it( "test 2 extractKeyValue should return undefined for no value", ( ) =>
44 | {
45 | let expectedKey = undefined;
46 | let value = undefined;
47 | let result = extractKeyValue( CMD4_ACC_TYPE_ENUM.properties["0"].validValues, value );
48 | assert.equal( result, expectedKey, "Test 1 extractKeyValue( " + value + " ) returned:" + result + " expected:" + expectedKey );
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/utils/indexOfEnumLintTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is not a utility. This is a testcase, but not a Unit test either.
4 | // Lint had picked up an error that indexOfEnum was set, but not used; though
5 | // the function worked perfectly as proven by unit testing.
6 | // What I had found is that without the Object.defineProperty within the
7 | // requiring code, like this one, lint would fail.
8 | //
9 | // This test is placed here, just to remember that reason.
10 |
11 |
12 | var HomebridgeAPI = require( "../node_modules/homebridge/lib/api" ).HomebridgeAPI;
13 | var _api = new HomebridgeAPI( ); // object we feed to Plugins
14 |
15 |
16 |
17 | var { indexOfEnum } = require( "../utils/indexOfEnum" );
18 |
19 | Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } });
20 |
21 | let ACC_DATA = require('../lib/CMD4_ACC_TYPE_ENUM');
22 | let DEVICE_DATA = require('../lib/CMD4_DEVICE_TYPE_ENUM');
23 |
24 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic );
25 | let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories );
26 |
27 |
28 | let i=0;
29 | let j = 63;
30 | let type;
31 | let ucKeyIndex;
32 |
33 | type = CMD4_DEVICE_TYPE_ENUM.properties[ j ].deviceName;
34 | console.log("checking type:" + type );
35 |
36 |
37 | ucKeyIndex = CMD4_DEVICE_TYPE_ENUM.properties.indexOfEnum( i => i.deviceName === type);
38 | if ( ucKeyIndex < 0 )
39 | {
40 | console.log("FAIL: Invalid device type:%s", type );
41 | } else {
42 | console.log("PASS (:%s) %s = %s", type, i, ucKeyIndex );
43 | }
44 |
45 | type="blah";
46 | console.log("checking type:" + type );
47 |
48 |
49 | ucKeyIndex = CMD4_DEVICE_TYPE_ENUM.properties.indexOfEnum( i => i.deviceName === type);
50 | if ( ucKeyIndex < 0 )
51 | {
52 | console.log("PASS: Invalid device type:%s", type );
53 | } else {
54 | console.log("FAIL: (%s) %s = %s", type, i, ucKeyIndex );
55 | }
56 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/support-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Support Request
3 | about: Stuck on one of the installation steps or having trouble with your script?
4 | title: "[Support]"
5 | labels: help wanted
6 | assignees: ztalbot2000
7 |
8 | ---
9 | ** Cmd4 No longer supported:**
10 |
11 |
12 |
13 |
14 |
15 |
16 | **Describe Your Problem:**
17 |
18 |
19 | [**Link to Logs:**]()
20 |
21 |
22 |
23 |
24 |
25 | **Paste of Logs:**
26 | ```
27 |
28 | ```
29 |
30 | **Cmd4 Config:**
31 |
32 |
33 |
34 | ```json
35 |
36 |
37 | ```
38 |
39 | **Screenshots:**
40 |
41 |
42 | **Environment:**
43 |
44 | * **Node.js Version**:
45 | * **NPM Version**:
46 | * **Homebridge Version**:
47 | * **homebridge-cmd4 Version**:
48 | * **Operating System**: Raspbian / Ubuntu / Debian / Windows / macOS / Docker / other
49 | * **Process Supervisor**: Systemd / init.d / pm2 / launchctl / Docker / hb-service / other / none
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/DoorLock.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # In this example we see a Raspberry Pi with a gpio pin that triggers
5 | # a lock mechanism. The lock is momentary. You should configure the GPIO by executing:
6 | #
7 | # echo 24 > /sys/class/gpio/export
8 | # echo "out" > /sys/class/gpio/gpio24/direction
9 | #
10 | # The corresponding config.json is
11 | #
12 | # {
13 | # "type": "LockMechanism",
14 | # "displayName": "Front Door",
15 | # "lockCurrentState": "SECURED",
16 | # "lockTargetState": "SECURED",
17 | # "name": "Front Door",
18 | # "manufacturer": "XYZ",
19 | # "model": "XYZ",
20 | # "serialNumber": "GPIO 24",
21 | # "polling": [
22 | # {
23 | # "characteristic": "lockCurrentState",
24 | # "interval": 5,
25 | # "timeout": 4900
26 | # },
27 | # {
28 | # "characteristic": "lockTargetState",
29 | # "interval": 5,
30 | # "timeout": 4900
31 | # }
32 | # ],
33 | # "stateChangeResponseTime": 0.2,
34 | # "state_cmd": "sh /homebridge/DoorLock.sh"
35 | # }
36 |
37 | #!/bin/sh
38 |
39 | STATE_FILE="/dev/shm/DoorLock.state"
40 |
41 | if [ ! -f "$STATE_FILE" ]; then
42 | echo 1 > $STATE_FILE
43 | fi
44 |
45 | STATE=$(cat $STATE_FILE)
46 |
47 | if [ "$1" = "Get" ]; then
48 | case $3 in
49 | "LockCurrentState")
50 | echo $STATE
51 | ;;
52 | "LockTargetState")
53 | echo $STATE
54 | echo 1 > $STATE_FILE
55 | ;;
56 | esac
57 | echo 0 > /sys/class/gpio/gpio24/value
58 | exit 0
59 | fi
60 |
61 | if [ "$1" = "Set" ]; then
62 | case $3 in
63 | "LockTargetState")
64 | if [ "$4" = "UNSECURED" ]; then
65 | echo 0 > $STATE_FILE
66 | echo 1 > /sys/class/gpio/gpio24/value
67 | sleep 0.1
68 | fi
69 | ;;
70 | esac
71 | echo 0 > /sys/class/gpio/gpio24/value
72 | exit 0
73 | fi
74 |
75 | exit 66
76 |
--------------------------------------------------------------------------------
/test/isCmd4Directive.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let { indexOfEnum } = require( "../utils/indexOfEnum" );
4 | Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } });
5 |
6 | let isCmd4Directive = require( "../utils/isCmd4Directive" );
7 |
8 |
9 | var _api = new HomebridgeAPI(); // object we feed to Plugins
10 |
11 | // Init the library for all to use
12 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms );
13 |
14 |
15 | // ******** QUICK TEST of SETUP *************
16 | describe('Quick Test of Setup', ( ) =>
17 | {
18 | it( `CMD4_ACC_TYPE_ENUM.EOL = ${ ACC_EOL }`, ( ) =>
19 | {
20 | expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL );
21 | });
22 | });
23 |
24 | // ******** TEST isCmd4Directive .*************
25 |
26 | describe( `Test isCmd4Directive import`, ( ) =>
27 | {
28 | it( `isCmd4Directive should be a function `, ( ) =>
29 | {
30 | assert.isFunction( isCmd4Directive, `isCmd4Directive is not a function. Found: ${ typeof isCmd4Directive }` );
31 | });
32 | });
33 |
34 | describe( `Test isCmd4Directive`, ( ) =>
35 | {
36 | it( `isCmd4Directive should identify a Cmd4 directive`, ( ) =>
37 | {
38 | let directive = "polling";
39 | let rc = isCmd4Directive( directive );
40 | assert.isString( rc.key, `Unexpected result for isCmd4Directive` );
41 | assert.equal( rc.key, directive, `Expected result to be "polling"` );
42 | });
43 | it( `isCmd4Directive should NOT identify an uppercase type `, ( ) =>
44 | {
45 | let directive = "Polling";
46 | let rc = isCmd4Directive( directive );
47 | assert.isNull( rc, `Expected result to be null` );
48 | });
49 | it( `isCmd4Directive should NOT identify an unknown type `, ( ) =>
50 | {
51 | let directive = "Blast";
52 | let rc = isCmd4Directive( directive );
53 | assert.isNull( rc, `Expected result to be null` );
54 | });
55 | it( `isCmd4Directive should identify an uppercase type if upperCase is checked`, ( ) =>
56 | {
57 | let directive = "Polling";
58 | let rc = isCmd4Directive( directive, true );
59 | assert.isString( rc.key, `Expected result to be a string` );
60 | assert.equal( rc.key, "polling", `Expected result to be "polling"` );
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/utils/VariableTimer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /* Orig
4 | var variableTimer = {
5 | running: false,
6 | iv: 5000,
7 | timeout: false,
8 | cb : function(){},
9 | start : function(cb,iv){
10 | var elm = this;
11 | clearInterval(this.timeout);
12 | this.running = true;
13 | if(cb) this.cb = cb;
14 | if(iv) this.iv = iv;
15 | this.timeout = setTimeout(function(){elm.execute(elm)}, this.iv);
16 | },
17 | execute : function(e){
18 | if(!e.running) return false;
19 | e.cb();
20 | e.start();
21 | },
22 | stop : function(){
23 | this.running = false;
24 | },
25 | set_interval : function(iv){
26 | clearInterval(this.timeout);
27 | this.start(false, iv);
28 | }
29 | };
30 | */
31 |
32 | // Taken from https://stackoverflow.com/questions/1280263/changing-the-interval-of-setinterval-while-its-running
33 | //
34 | // timer.start(function(){
35 | // console.debug('go');
36 | // }, 2000);
37 | //
38 | // timer.set_interval(500);
39 | //
40 | // timer.stop();
41 | //
42 |
43 | class VariableTimer
44 | {
45 | constructor ()
46 | {
47 | this.running = false;
48 | this.iv = 0;
49 | this.timeout = false;
50 | this.cb = function( ){ };
51 | }
52 | start( cb , iv )
53 | {
54 | var elm = this;
55 | clearInterval( this.timeout );
56 | this.running = true;
57 | if ( cb ) this.cb = cb;
58 | if ( iv ) this.iv = iv;
59 | this.timeout = setTimeout( function( ){ elm.execute( elm ) }, this.iv );
60 | }
61 |
62 | execute( e )
63 | {
64 | if ( ! e.running ) return false;
65 | e.cb( );
66 | e.start( );
67 | }
68 |
69 | stop( )
70 | {
71 | this.running = false;
72 | }
73 |
74 | set_interval( iv )
75 | {
76 | // You can't change an interval when timer is not running
77 | if ( this.running == false )
78 | {
79 | this.iv = iv;
80 | return;
81 | }
82 |
83 | // Do not change interval if less than .5 seconds difference
84 | let round = Math.trunc( iv / 500) * 500;
85 | if (round != this.iv )
86 | {
87 | clearInterval( this.timeout );
88 | this.start( false, round );
89 | }
90 | }
91 | }
92 |
93 | //module.exports.VariableTimer = VariableTimer;
94 | module.exports = VariableTimer;
95 |
--------------------------------------------------------------------------------
/test/versionChecker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { isUpgrade, getLatestVersion, isVersionNewerThanPackagedVersion, getPackagedVersion } = require( "../utils/versionChecker" );
4 |
5 | describe( `Testing versionChecker init`, ( ) =>
6 | {
7 | it( `isUpgrade should be a function`, ( ) =>
8 | {
9 | assert.isFunction( isUpgrade, `isUpgrade is not a function` );
10 | });
11 |
12 | it( `getLatestVersion should be a function`, ( ) =>
13 | {
14 | assert.isFunction( getLatestVersion, `getLatestVersion is not a function` );
15 | });
16 |
17 | it( `isVersionNewerThanPackagedVersion should be a function`, ( ) =>
18 | {
19 | assert.isFunction( isVersionNewerThanPackagedVersion, `isVersionNewerThanPackagedVersion is not a function` );
20 | });
21 |
22 | it( `getPackagedVersion should be a function`, ( ) =>
23 | {
24 | assert.isFunction( getPackagedVersion, `getPackagedVersion is not a function` );
25 | });
26 | });
27 |
28 |
29 | describe( `Testing versionChecker functionality`, ( ) =>
30 | {
31 | it( `getPackagedVersion should return an string`, ( ) =>
32 | {
33 | let result = getPackagedVersion( );
34 | assert.isString( result, `getPackagedVersion failed: ${ typeof result }` );
35 | });
36 |
37 | it( `getLatestVersion should return an string`, async ( ) =>
38 | {
39 | let result = await getLatestVersion( );
40 | assert.isString( result, `getLatestVersion failed: ${ typeof result }` );
41 | }).timeout(5000);
42 |
43 | it( `isVersionNewerThanPackagedVersion should return true for a high version`, ( ) =>
44 | {
45 | let result = isVersionNewerThanPackagedVersion( "9.0.0" );
46 | assert.isTrue( result, `isVersionNewerThanPackagedVersion expected: true: found: ${ result }` );
47 | });
48 | it( `isVersionNewerThanPackagedVersion should return false for a lower version`, ( ) =>
49 | {
50 | let result = isVersionNewerThanPackagedVersion( "1.0.0" );
51 | assert.isFalse( result, `isVersionNewerThanPackagedVersion expected: false: found: ${ result }` );
52 | });
53 |
54 | it( `isVersionNewerThanPackagedVersion should return false for same version`, async ( ) =>
55 | {
56 | let latest = await getLatestVersion( );
57 | let result = isVersionNewerThanPackagedVersion( latest );
58 | assert.isFalse( result, `isVersionNewerThanPackagedVersion expected: false: found: ${ result }` );
59 | }).timeout(5000);
60 |
61 | })
62 |
63 |
--------------------------------------------------------------------------------
/test/isAccDirective.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let { indexOfEnum } = require( "../utils/indexOfEnum" );
4 | Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } });
5 |
6 | let isAccDirective = require( "../utils/isAccDirective" );
7 |
8 |
9 | var _api = new HomebridgeAPI(); // object we feed to Plugins
10 |
11 | // Init the library for all to use
12 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms );
13 |
14 |
15 | // ******** TEST isAccDirectivw .*************
16 |
17 | describe( `Test isAccDirective import`, ( ) =>
18 | {
19 | it( `isAccDirectivw should be a function `, ( ) =>
20 | {
21 | assert.isFunction( isAccDirective, `isAccDirective is not a function. Found: ${ typeof isAccDirective }` );
22 | });
23 | });
24 |
25 | describe( `Test isAccDirective`, ( ) =>
26 | {
27 | it( `isAccDirective should identify a type `, ( ) =>
28 | {
29 | let type = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On );
30 | let rc = isAccDirective( type );
31 | assert.isString( rc.type, `Unexpected result for isAccDirective` );
32 | assert.equal( rc.type, type, `Expected result to be "on"` );
33 | assert.equal( rc.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `UnExpected result` );
34 | });
35 | it( `isAccDirective should NOT identify an uppercase type `, ( ) =>
36 | {
37 | let ucType = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On );
38 | let rc = isAccDirective( ucType );
39 | assert.isNull( rc.accTypeEnumIndex, `Expected result to be null` );
40 | });
41 | it( `isAccDirective should NOT identify an unknown type `, ( ) =>
42 | {
43 | let type = "Blast";
44 | let rc = isAccDirective( type );
45 | assert.isNull( rc.accTypeEnumIndex, `Expected result to be null` );
46 | });
47 | it( `isAccDirective should identify an uppercase type if upperCase is checked`, ( ) =>
48 | {
49 | let type = CMD4_ACC_TYPE_ENUM.accEnumIndexToLC( CMD4_ACC_TYPE_ENUM.On );
50 | let ucType = CMD4_ACC_TYPE_ENUM.accEnumIndexToUC( CMD4_ACC_TYPE_ENUM.On );
51 |
52 | let rc = isAccDirective( ucType, true );
53 | assert.isString( rc.type, `Unexpected result for isAccDirective` );
54 | assert.equal( rc.type, type, `Expected result to be "on"` );
55 | assert.equal( rc.accTypeEnumIndex, CMD4_ACC_TYPE_ENUM.On, `UnExpected result` );
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/isJSON.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var isJSON = require( "../utils/isJSON.js" );
4 |
5 | describe( "Testing isJSON", ( ) =>
6 | {
7 | it( "isJSON should be a function", ( ) =>
8 | {
9 | assert.isFunction( isJSON, "isJSON is not a function" );
10 | });
11 |
12 | it( "isJSON should correctly identify a JSON object", ( ) =>
13 | {
14 | let data = { "name": "John"};
15 | let expectedResult = true;
16 | let result = isJSON( data );
17 | assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult );
18 | });
19 |
20 | it( "isJSON should return false for a string", ( ) =>
21 | {
22 | let data = "abcdef";
23 | let expectedResult = false;
24 | let result = isJSON( data );
25 | assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult );
26 | });
27 |
28 | it( "isJSON should return false for a number", ( ) =>
29 | {
30 | let data = 12345;
31 | let expectedResult = false;
32 | let result = isJSON( data );
33 | assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult );
34 | });
35 |
36 | it( "isJSON should return false for a float", ( ) =>
37 | {
38 | let data = 3.1415;
39 | let expectedResult = false;
40 | let result = isJSON( data );
41 | assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult );
42 | });
43 |
44 | it( "isJSON should return false for a boolean", ( ) =>
45 | {
46 | let data = false;
47 | let expectedResult = false;
48 | let result = isJSON( data );
49 | assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult );
50 | });
51 |
52 | it( "isJSON should return false for an array", ( ) =>
53 | {
54 | let data = [1,2,3,4];
55 | let expectedResult = false;
56 | let result = isJSON( data );
57 | assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult );
58 | });
59 |
60 | it( "isJSON should return false for a undefined", ( ) =>
61 | {
62 | let data = undefined;
63 | let expectedResult = false;
64 | let result = isJSON( data );
65 | assert.equal( result, expectedResult, "isJSON( " + data + " ) returned: " + result + " expected: " + expectedResult );
66 | });
67 | })
68 |
69 |
--------------------------------------------------------------------------------
/test/echoScripts/testGetSetValues.js:
--------------------------------------------------------------------------------
1 | #!/opt/homebrew/bin/node
2 |
3 | const fs = require( "fs" );
4 |
5 | function showHelp()
6 | {
7 | console.log(`
8 | Syntax: Get Device < fn >
9 | Set Device [< value >] < fn >
10 |
11 |
12 | Set writes stuff to < fn > as a required fn with
13 | INPUTS.IO
14 | INPUTS.DEVICE
15 | INPUTS.CHARACTERISTIC
16 | INPUTS.VALUE
17 |
18 | Get echos what Set would have sent
19 |
20 |
21 | ` );
22 | process.exit( 666 );
23 | }
24 |
25 | function createDeviceFileName( fn, DEVICE, CHARACTERISTIC )
26 | {
27 | return `${ fn }_${ DEVICE }_${ CHARACTERISTIC }`;
28 | }
29 |
30 | function doGet( )
31 | {
32 | if ( ELEMENTS != 6 ) { showHelp( ); process.exit( 1 ); }
33 |
34 | let fn = process.argv[5];
35 |
36 | let deviceFile = createDeviceFileName( fn, DEVICE, CHARACTERISTIC );
37 |
38 | let INPUTS;
39 |
40 | try {
41 | INPUTS = require( deviceFile );
42 | } catch ( e ) {
43 | console.log(`Cannot load fn: ${ deviceFile }, error: ${ e }`);
44 | process.exit( 666 );
45 | }
46 |
47 | console.log( `${ INPUTS.VALUE }` );
48 |
49 | process.exit( 0 );
50 | }
51 |
52 | function doSet( )
53 | {
54 | if ( ELEMENTS != 7 ) { showHelp( ); process.exit( 1 ); };
55 |
56 | let VALUE = process.argv[5];
57 | let fn = process.argv[6];
58 |
59 | let deviceFile = createDeviceFileName( fn, DEVICE, CHARACTERISTIC );
60 |
61 | // Skip the fn
62 | ELEMENTS--;
63 |
64 | // Remove the old fn
65 | try {
66 | fs.unlinkSync( deviceFile );
67 | } catch(err) {
68 | // Don't care
69 | // console.error( err )
70 | // process.exit( 666 );
71 | }
72 |
73 | let data = `exports.IO="${ IO }";
74 | exports.DEVICE="${ DEVICE }";
75 | exports.CHARACTERISTIC="${ CHARACTERISTIC }";
76 | exports.VALUE="${ VALUE }";\n`;
77 |
78 | // Write the arguments as requires
79 | fs.writeFileSync( deviceFile, data, function( err )
80 | {
81 | console.log(` Error writing to fn: ${ deviceFile } error: ${ err }\n`);
82 | process.exit( 666 );
83 | });
84 |
85 | process.exit( 0 );
86 | }
87 |
88 | let ELEMENTS = process.argv.length;
89 | if ( ELEMENTS < 4 ) { showHelp( ); process.exit( 1 ); }
90 |
91 | let IO = process.argv[2];
92 | let DEVICE = process.argv[3];
93 | let CHARACTERISTIC = process.argv[4];
94 |
95 | switch( IO )
96 | {
97 | case "Get":
98 | doGet( );
99 | break;
100 | case "Set":
101 | doSet( );
102 | break;
103 | default:
104 | console.log(`Invalid Get/Set: ${IO}` );
105 | showHelp();
106 | }
107 | process.exit( 1 );
108 |
--------------------------------------------------------------------------------
/test/isDevDirective.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let { indexOfEnum } = require( "../utils/indexOfEnum" );
4 | Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } });
5 |
6 | let isDevDirective = require( "../utils/isDevDirective" );
7 | let lcFirst = require( "../utils/lcFirst" );
8 |
9 |
10 | var _api = new HomebridgeAPI(); // object we feed to Plugins
11 |
12 | // Init the library for all to use
13 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms );
14 | let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories );
15 |
16 |
17 |
18 | // ******** TEST isDevDirectivw .*************
19 |
20 | describe( `Test isDevDirective import`, ( ) =>
21 | {
22 | it( `isDevDirectivw should be a function `, ( ) =>
23 | {
24 | assert.isFunction( isDevDirective, `isDevDirective is not a function. Found: ${ typeof isDevDirective }` );
25 | });
26 | });
27 |
28 | describe( `Test isDevDirective`, ( ) =>
29 | {
30 | it( `isDevDirective should identify a device `, ( ) =>
31 | {
32 | let deviceName = CMD4_DEVICE_TYPE_ENUM.devEnumIndexToC( CMD4_DEVICE_TYPE_ENUM.Switch );
33 | let rc = isDevDirective( deviceName );
34 | assert.isString( rc.deviceName, `Unexpected result for isDevDirective` );
35 | assert.equal( rc.deviceName, deviceName, `Expected result to be "on"` );
36 | assert.equal( rc.devEnumIndex, CMD4_DEVICE_TYPE_ENUM.Switch, `UnExpected result` );
37 | });
38 | it( `isDevDirective should NOT identify an lowercase deviceName `, ( ) =>
39 | {
40 | let deviceName = CMD4_DEVICE_TYPE_ENUM.devEnumIndexToC( CMD4_DEVICE_TYPE_ENUM.Switch );
41 | let lcDeviceName = lcFirst( deviceName );
42 | let rc = isDevDirective( lcDeviceName );
43 | assert.isNull( rc.devEnumIndex, `Expected result to be null` );
44 | });
45 | it( `isDevDirective should NOT identify an unknown deviceName `, ( ) =>
46 | {
47 | let deviceName = "Blast";
48 | let rc = isDevDirective( deviceName );
49 | assert.isNull( rc.devEnumIndex, `Expected result to be null` );
50 | });
51 | it( `isDevDirective should identify an lower case deviceName if lowerCase is checked`, ( ) =>
52 | {
53 | let deviceName = CMD4_DEVICE_TYPE_ENUM.devEnumIndexToC( CMD4_DEVICE_TYPE_ENUM.Switch );
54 | let lcDeviceName = lcFirst( deviceName );
55 | let rc = isDevDirective( lcDeviceName, true );
56 |
57 | assert.isString( rc.deviceName, `Unexpected result for isDevDirective` );
58 | assert.equal( rc.deviceName, deviceName, `Expected result to be "on"` );
59 | assert.equal( rc.devEnumIndex, CMD4_DEVICE_TYPE_ENUM.Switch, `UnExpected result` );
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/utils/HV.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const constants = require( "../cmd4Constants" );
4 |
5 | // These would already be initialized by index.js
6 | let CMD4_DEVICE_TYPE_ENUM = require( "../lib/CMD4_DEVICE_TYPE_ENUM" ).CMD4_DEVICE_TYPE_ENUM;
7 |
8 | class HV
9 | {
10 | constructor ()
11 | {
12 | this.allowTLV8 = constants.DEFAULT_ALLOW_TLV8;
13 | this.debug = constants.DEFAULT_DEBUG;
14 | this.outputConstants = constants.DEFAULT_OUTPUTCONSTANTS;
15 | this.interval = constants.DEFAULT_INTERVAL;
16 | this.stateChangeResponseTime = constants.DEFAULT_STATE_CHANGE_RESPONSE_TIME;
17 | this.statusMsg = constants.DEFAULT_STATUSMSG;
18 | this.timeout = constants.DEFAULT_TIMEOUT;
19 |
20 | this.stateChangeResponseTimeHasBeenUpdated = false;
21 | }
22 |
23 | update( entity )
24 | {
25 | // Heirarchy Next the Device Properties
26 | if ( entity.typeIndex != undefined &&
27 | this.stateChangeResponseTimeHasBeenUpdated == false )
28 | {
29 | this.stateChangeResponseTime = CMD4_DEVICE_TYPE_ENUM.properties[ entity.typeIndex ].devicesStateChangeDefaultTime;
30 | }
31 |
32 | // FakeGato Hierarchy
33 | if ( entity.storage != undefined )
34 | this.storage = entity.storage;
35 | if ( entity.storagePath != undefined )
36 | this.storagePath = entity.storagePath;
37 | if ( entity.folder != undefined )
38 | this.folder = entity.folder;
39 | if ( entity.keyPath != undefined )
40 | this.keyPath = entity.keyPath;
41 |
42 | // Heirarchy
43 | if ( entity.allowTLV8 != undefined )
44 | this.allowTLV8 = entity.allowTLV8;
45 |
46 |
47 | if ( entity.debug != undefined )
48 | this.debug = entity.debug;
49 | if ( entity.definitions != undefined )
50 | this.definitions = entity.definitions;
51 | if ( entity.interval != undefined )
52 | this.interval = entity.interval;
53 | if ( entity.outputConstants != undefined )
54 | this.outputConstants = entity.outputConstants;
55 | if ( entity.queueTypes != undefined )
56 | this.queueTypes = entity.queueTypes;
57 | if ( entity.stateCmd != undefined )
58 | this.stateCmd = entity.stateCmd;
59 | if ( entity.state_cmd_prefix != undefined )
60 | this.state_cmd_prefix = entity.state_cmd_prefix;
61 | if ( entity.stateCmdSuffix != undefined )
62 | this.state_cmd_suffix = entity.state_cmd_suffix;
63 | if ( entity.stateChangeResponseTime != undefined )
64 | {
65 | this.stateChangeResponseTimeHasBeenUpdated = true;
66 | this.stateChangeResponseTime = entity.stateChangeResponseTime;
67 | }
68 | if ( entity.statusMsg != undefined )
69 | this.statusMsg = entity.statusMsg;
70 | if ( entity.timeout != undefined )
71 | this.timeout = entity.timeout;
72 | }
73 |
74 | }
75 |
76 | module.exports = HV;
77 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/basic_ping.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # This Cmd4 example demonstrates how you can test if an accessory is on the network using ping.
5 | #
6 | # Your Cmd4 .homebridge/.config.json file would have a state_cmd like:
7 | # state_cmd: ".homebridge/Cmd4Scripts/Examples/ping.sh"
8 | #
9 | #
10 | # Testing from the shell prompt:
11 | # ./basic_ping.sh Get My_TV On
12 | # or
13 | # ./basic_ping.sh Set My_TV On 1
14 |
15 |
16 |
17 | # Exit immediately if a command exits with a non-zero status
18 | set -e
19 |
20 | # Define the accessories IP you wish to test
21 | ip="192.168.2.1"
22 |
23 | # Check if the first parameter to this script was "Get" for getting an accessory's
24 | # specific attribute.
25 | if [ "$1" = "Get" ]; then
26 |
27 | # Normally we would exit immediately if a command fails with a non-zero status.
28 | # In this case ping can fail and we would rely on the failing exit status to
29 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
30 | # thing to do. However for this example we are going to output '0' (false) so
31 | # that you can see the '0' on the console telling us that the accessory is not
32 | # on the network.
33 | set +e
34 |
35 | # $2 would be the name of the accessory
36 | # $3 would be the accessory's charactersistic 'On'
37 | # On OSX the string is returned differently than on linux.
38 | ping -c 2 -W 1 "${ip}" | sed -E 's/2 packets received/2 received/g' | grep -i '2 received' >> /dev/null
39 | rc=$?
40 |
41 | # Exit immediately if a command exits with a non-zero status
42 | set -e
43 |
44 | # Check if we got the message '2 packets recieved' meaning the accessory is
45 | # on the network by seeing if the return code of the above command passed or
46 | # failed.
47 | if [ "$rc" = "0" ]; then
48 | # The message was recieved so the target is up, sending a '1' (true), like
49 | # a binary number is, back to Cmd4.
50 | echo "1"
51 |
52 | # Exit this script positivitely.
53 | exit 0
54 | else
55 | # The message was not recieved so the target must be down, sending a '0' (false), like
56 | # a binary number is, back to Cmd4.
57 | echo "0"
58 |
59 | # Exit this script positivitely, even though ping failed.
60 | exit 0
61 | fi
62 | fi
63 |
64 | # Check if the first parameter to this script was "Set" for setting an accessory's
65 | # specific attribute.
66 | if [ "$1" = "Set" ]; then
67 |
68 | # $2 would be the name of the accessory.
69 | # $3 would be the accessory's charactersistic 'On'.
70 | # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is.
71 | # $4 would be 'true' for 'On' and 'false' for 'Off' with
72 | # outputConstants=true in your .homebridge/.config.json file.
73 |
74 | # This ping script does not do anything for set so just exit successfully.
75 | exit 0
76 | fi
77 |
78 | # The proper arguments to this script were not passed to it so end with a failure exit status.
79 | exit 66
80 |
--------------------------------------------------------------------------------
/test/VariableTimer.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | let VariableTimer = require( "../utils/VariableTimer" );
5 |
6 |
7 | describe('A Variable Timer Test', ( ) =>
8 | {
9 | it( "Test creation of variableTimer", ( ) =>
10 | {
11 | const timer = new VariableTimer( );
12 |
13 | assert.instanceOf( timer , VariableTimer, "Expected timer to be instance of VariableTimer. Found %s" , timer );
14 | assert.isFunction( timer.start, ".start is not a function" );
15 | assert.isFunction( timer.stop, ".stop is not a function" );
16 | assert.isFunction( timer.set_interval, ".set_interval is not a function" );
17 |
18 | });
19 |
20 | // Only skip this test because it takes 20 seconds
21 | it.skip( "Test timer can change intervals, stop start ...", ( done ) =>
22 | {
23 | const timer = new VariableTimer( );
24 | var start = new Date();
25 | var total = 0;
26 |
27 | timer.start( ( ) =>
28 | {
29 | var end = new Date();
30 | var seconds = ( end - start) / 1000
31 | total += Math.trunc( seconds );
32 | start = end;
33 | console.log(`done ${ seconds } ${ total }`);
34 | }, 1000);
35 | assert.equal( timer.iv, 1000, "iv is set incorrectly" );
36 | setTimeout(() =>
37 | {
38 | console.log("changing timer to 2 seconds.");
39 | timer.set_interval( 2000 );
40 | assert.equal( timer.iv, 2000, "iv is reset incorrectly" );
41 | assert.equal( total, 4, "total is incorrect" );
42 | setTimeout(() =>
43 | {
44 | assert.equal( total, 12, "total is incorrect" );
45 | timer.stop();
46 | done();
47 | }, 10000);
48 | }, 5000);
49 |
50 | }).timeout(20000);
51 |
52 | it( "Test timer can change intervals, only by .5s increments", ( done ) =>
53 | {
54 | const timer = new VariableTimer( );
55 | var start = new Date();
56 | var total = 0;
57 |
58 | timer.start( ( ) =>
59 | {
60 | var end = new Date();
61 | var seconds = ( end - start) / 1000
62 | total += Math.trunc( seconds );
63 | start = end;
64 | console.log(`done ${ seconds } ${ total }`);
65 | }, 1000);
66 | assert.equal( timer.iv, 1000, "iv is set incorrectly" );
67 | timer.set_interval( 2000 );
68 | assert.equal( timer.iv, 2000, "iv reset to 2000 incorrectly" );
69 | timer.set_interval( 2100 );
70 | assert.equal( timer.iv, 2000, "iv reset incorrectly to 2100" );
71 | timer.set_interval( 2500 );
72 | assert.equal( timer.iv, 2500, "iv reset incorrectly to 2500" );
73 | timer.set_interval( 2999 );
74 | assert.equal( timer.iv, 2500, "iv reset incorrectly to 2999" );
75 | timer.set_interval( 3000 );
76 | assert.equal( timer.iv, 3000, "iv reset incorrectly to 3000" );
77 | timer.set_interval( 2500 );
78 | assert.equal( timer.iv, 2500, "iv reset incorrectly down to 2500" );
79 | timer.stop();
80 | done( );
81 |
82 | }).timeout(20000);
83 | });
84 |
85 |
--------------------------------------------------------------------------------
/utils/versionChecker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // These routines are used to get this packages
4 | // version information.
5 | // It not only uses Promises but creates Promises
6 | // and is documented as such.
7 |
8 | const latestVersion = require( "latest-version" );
9 |
10 | // Retrieve the package information that contains
11 | // things like the current version.
12 | const myPkg = require( "../package.json" );
13 |
14 | // Split the version into its sub components.
15 | function splitVersion( version )
16 | {
17 | let parts = version.split( "." );
18 | return { "version": parts[ 0 ], "major": parts[ 1 ], "minor": parts[ 2 ] };
19 | }
20 |
21 | // getLatestVersion could just be defined as:
22 | // function getLatestVersion ( )
23 | // However, defining it this way signifies it
24 | // returns a Promise. In this case the Promise
25 | // given to us by latestVersion.
26 | const getLatestVersion = async ( ) =>
27 | {
28 | return latestVersion( myPkg.name );
29 | }
30 |
31 | function getPackagedVersion( )
32 | {
33 | return myPkg.version;
34 | }
35 |
36 | // Check that there is a possible upgrade out there.
37 | function isVersionNewerThanPackagedVersion( version )
38 | {
39 | // The default return code.
40 | let rc = false;
41 |
42 | // Split the version components into their sub components
43 | let installedVersionInfo = splitVersion( myPkg.version );
44 | let gitVersionInfo = splitVersion( version );
45 |
46 | // Set the return code appropriately
47 | if ( Number( gitVersionInfo.version ) > Number( installedVersionInfo.version ) )
48 | return true;
49 | if ( Number( gitVersionInfo.version ) < Number( installedVersionInfo.version ) )
50 | return false;
51 | if ( Number( gitVersionInfo.major ) > Number( installedVersionInfo.major ) )
52 | return true;
53 | if ( Number( gitVersionInfo.major ) < Number( installedVersionInfo.major ) )
54 | return false;
55 | if ( Number( gitVersionInfo.minor ) > Number( installedVersionInfo.minor ) )
56 | return true;
57 |
58 | return rc;
59 | }
60 |
61 |
62 | // Check that there is a possible upgrade out there.
63 | function isUpgrade( )
64 | {
65 | // Create a new Promise that will be fufilled when we processed the
66 | // information provided to us by the Promise of getLatestVersion.
67 | // You cannot take an asynchronous call and convert it to a synchronous
68 | // call unless you would create a timer and wait forever? for it
69 | // to complete, which defeats the purpose of node.js.
70 | return new Promise( ( resolve ) =>
71 | {
72 | // To use the promise of getLatestVersion, it must be in an async function
73 | // so put it in one.
74 | ( async( ) =>
75 | {
76 | // Wait for the Promise of getLatestVersion to complete
77 | let lv = await getLatestVersion( );
78 |
79 | resolve( isVersionNewerThanPackagedVersion( lv ) );
80 | }
81 | )( );
82 | });
83 | }
84 |
85 | // Export the internal functions we wish to expose.
86 | module.exports = { isUpgrade, getLatestVersion, isVersionNewerThanPackagedVersion, getPackagedVersion };
87 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/advanced_ping.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # This Cmd4 example demonstrates a little more advanced way of using ping to test if an
5 | # accessory is on the network by passing in the IP address to be used with the Cmd4 option
6 | # of state_cmd_suffix.
7 | #
8 | # Your Cmd4 .homebridge/.config.json file would have a state_cmd like:
9 | # state_cmd: ".homebridge/Cmd4Scripts/Examples/ping.sh"
10 | # state_cmd_suffix: "192.168.2.1"
11 | #
12 | # Testing from the shell prompt:
13 | # ./advanced_ping.sh Get My_TV On 192.168.2.1
14 | # or
15 | # ./advanced_ping.sh Set My_TV On 1 192.168.2.1
16 |
17 | # Exit immediately if a command exits with a non-zero status
18 | set -e
19 |
20 | # Check if the first parameter to this script was "Get" for getting an accessory's
21 | # specific attribute.
22 | if [ "$1" = "Get" ]; then
23 |
24 | # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fourth
25 | # parameter to a Get command.
26 | ip="${4}"
27 |
28 | # Normally we would exit immediately if a command fails with a non-zero status.
29 | # In this case ping can fail and we would rely on the failing exit status to
30 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
31 | # thing to do. However for this example we are going to output '0' (false) so
32 | # that you can see the '0' on the console telling us that the accessory is not
33 | # on the network.
34 | set +e
35 |
36 | # $2 would be the name of the accessory
37 | # $3 would be the accessory's charactersistic 'On'
38 | # On OSX the string is returned differently than on linux.
39 | ping -c 2 -W 1 "${ip}" | sed -E 's/2 packets received/2 received/g' | grep -i '2 received' >> /dev/null
40 | rc=$?
41 |
42 | # Exit immediately if a command exits with a non-zero status
43 | set -e
44 |
45 | # Check if we got the message '2 packets recieved' meaning the accessory is
46 | # on the network by seeing if the return code of the above command passed or
47 | # failed.
48 | if [ "$rc" = "0" ]; then
49 | # The message was recieved so the target is up, sending a '1' (true), like
50 | # a binary number is, back to Cmd4.
51 | echo "1"
52 |
53 | # Exit this script positivitely.
54 | exit 0
55 | else
56 | # The message was not recieved so the target must be down, sending a '0' (false), like
57 | # a binary number is, back to Cmd4.
58 | echo "0"
59 |
60 | # Exit this script positivitely, even though ping failed.
61 | exit 0
62 | fi
63 | fi
64 |
65 | # Check if the first parameter to this script was "Set" for setting an accessory's
66 | # specific attribute.
67 | if [ "$1" = "Set" ]; then
68 |
69 | # $2 would be the name of the accessory.
70 | # $3 would be the accessory's charactersistic 'On'.
71 | # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is.
72 | # $4 would be 'true' for 'On' and 'false' for 'Off' with
73 | # outputConstants=true in your .homebridge/.config.json file.
74 |
75 | # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fifth
76 | # parameter to a Set command.
77 | ip="${5}"
78 |
79 | # This ping script does not do anything for set so just exit successfully.
80 | exit 0
81 | fi
82 |
83 | # The proper arguments to this script were not passed to it so end with a failure exit status.
84 | exit 66
85 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | //
4 | // Homebridge
5 | // Flow / \
6 | // / \
7 | // api.registerPlatform api.registerAccessory
8 | // forEach Accessories{ } Any { } before/after Accessories{ }
9 | // Cmd4Platform Cmd4Accessory
10 | // Cmd4Accessory
11 | //
12 | //
13 | //
14 | //
15 | //
16 | //
17 | //
18 | //
19 | //
20 | //
21 |
22 | // The Cmd4 Classes
23 | const Cmd4Accessory = require( "./Cmd4Accessory" ).Cmd4Accessory;
24 | const Cmd4Platform = require( "./Cmd4Platform" ).Cmd4Platform;
25 |
26 | const settings = require( "./cmd4Settings" );
27 |
28 | // Pretty colors
29 | const chalk = require( "chalk" );
30 |
31 | // The Library files that know all.
32 | var CHAR_DATA = require( "./lib/CMD4_CHAR_TYPE_ENUMS" );
33 | var ACC_DATA = require( "./lib/CMD4_ACC_TYPE_ENUM" );
34 | var DEVICE_DATA = require( "./lib/CMD4_DEVICE_TYPE_ENUM" );
35 |
36 | module.exports =
37 | {
38 | default: function ( api )
39 | {
40 | // Init the libraries for all to use
41 | let CMD4_CHAR_TYPE_ENUMS = CHAR_DATA.init( api.hap.Formats, api.hap.Units, api.hap.Perms );
42 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( api.hap.Characteristic, api.hap.Formats, api.hap.Units, api.hap.Perms );
43 | let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init(
44 | CMD4_ACC_TYPE_ENUM, api.hap.Service, api.hap.Characteristic, api.hap.Categories );
45 |
46 | api.registerAccessory( settings.PLATFORM_NAME, Cmd4Accessory );
47 | api.registerPlatform( settings.PLATFORM_NAME, Cmd4Platform );
48 |
49 | setTimeout( checkForUpdates, 1800 );
50 |
51 | // This is not required by homebridge and does not affect it. I use it for
52 | // unit testing.
53 | return { CMD4_CHAR_TYPE_ENUMS,
54 | CMD4_ACC_TYPE_ENUM,
55 | CMD4_DEVICE_TYPE_ENUM,
56 | api
57 | };
58 | },
59 | // These would be the uninitialized values,
60 | // used for unit testing
61 | CHAR_DATA: CHAR_DATA, // properties would be { } empty.
62 | ACC_DATA: ACC_DATA, // properties would be { } empty.
63 | DEVICE_DATA: DEVICE_DATA // properties would be { } empty.
64 | }
65 |
66 | function checkForUpdates( )
67 | {
68 | // Don't show the updates message in mocha test mode
69 | if ( process.argv.includes( "test/mocha-setup" ) )
70 | return;
71 |
72 | const { getLatestVersion, isVersionNewerThanPackagedVersion } = require( "./utils/versionChecker" );
73 | const myPkg = require( "./package.json" );
74 |
75 | ( async( ) =>
76 | {
77 | // Fix for #127, constant crash loops when no internet connection
78 | // trying to get latest Cmd4 version.
79 | // thx nano9g
80 | try
81 | {
82 | let lv = await getLatestVersion( );
83 |
84 | if ( isVersionNewerThanPackagedVersion( lv ) )
85 | {
86 | console.log( chalk.green( `[UPDATE AVAILABLE] ` ) + `Version ${lv} of ${myPkg.name} is available. Any release notes can be found here: ` + chalk.underline( `${myPkg.changelog}` ) );
87 | }
88 |
89 | }
90 | catch( error )
91 | {
92 | console.log( chalk.yellow( `[UPDATE CHECK FAILED] ` ) + `Could not check for newer versions of ${myPkg.name} due to error ${error.name}: ${error.message}`)
93 | }
94 | })( );
95 | }
96 |
--------------------------------------------------------------------------------
/test/loadPluginTest.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // ***************** TEST LOADING **********************
4 |
5 |
6 | var pluginModule = require( "../index" );
7 | var CMD4_ACC_TYPE_ENUM = pluginModule.ACC_DATA.CMD4_ACC_TYPE_ENUM;
8 | var CMD4_DEVICE_TYPE_ENUM = pluginModule.DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM;
9 |
10 | // While the above method is better, It doesn't check that
11 | // the key values are sequential.
12 | // You cannot break out of forEach ....
13 | function getIndexOfValue( obj, value )
14 | {
15 | let count = 0;
16 | let found = -1;
17 | Object.keys( obj ).forEach( function( key )
18 | {
19 | // console.log( "Checking: " + key + " count: " + count + " obj[key]: " + obj[key] + " for: " + value + " t1: " + typeof obj[key] + " t2: " + typeof value );
20 | if ( obj[key] == value && value == count ) { found = value }
21 | count+=1;
22 | });
23 | return found;
24 | }
25 |
26 | // ************ TEST PLUGIN WAS Loaded Successfully **************
27 | describe( "Testing load of index.js", ( ) =>
28 | {
29 | it( "Testing require of index.js", ( ) =>
30 | {
31 | expect( pluginModule ).not.to.be.a( "null", "loaded plugin was null" );
32 | });
33 |
34 | it( "index.js default initializer should be found", ( ) =>
35 | {
36 | expect( pluginModule.default ).to.be.a( "function", "plugin has no default init function t: " + typeof pluginModule.default);
37 | });
38 | });
39 |
40 | // ************ TEST UNINITIALIZED PLUGIN **************
41 | describe( "Testing uninitialized plugin", ( ) =>
42 | {
43 | // DEVICE_TYPE Testing
44 | it( "CMD4_DEVICE_TYPE_ENUM is defined", ( ) =>
45 | {
46 | expect( CMD4_DEVICE_TYPE_ENUM ).not.to.be.a( "null", "CMD4_DEVICE_TYPE_ENUM is null" );
47 | });
48 | it( "CMD4_DEVICE_TYPE_ENUM has EOL", ( ) =>
49 | {
50 | expect( CMD4_DEVICE_TYPE_ENUM.EOL ).not.to.be.a( "null", "CMD4_DEVICE_TYPE_ENUM.EOL is null" );
51 | });
52 |
53 | it( "CMD4_DEVICE_TYPE_ENUM.EOL = " + DEVICE_EOL, ( ) =>
54 | {
55 | expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL, "CMD4_DEVICE_TYPE_ENUM.EOL. Expected: " + DEVICE_EOL + " found: " + CMD4_DEVICE_TYPE_ENUM.EOL );
56 | });
57 |
58 | it( "CMD4_DEVICE_TYPE_ENUM[ 0-" + CMD4_DEVICE_TYPE_ENUM.EOL + " ] to have a valid value", ( ) =>
59 | {
60 | for ( let index=0; index < CMD4_DEVICE_TYPE_ENUM.EOL; index ++)
61 | {
62 | let keyIndex = getIndexOfValue( CMD4_DEVICE_TYPE_ENUM, index );
63 | expect( keyIndex ).to.equal( index, "Expected value at index: " + index + " to be: " + index + " found: " + keyIndex );
64 | }
65 | });
66 |
67 | // ACC_TYPE Testing
68 | it( "CMD4_ACC_TYPE_ENUM is defined", ( ) =>
69 | {
70 | expect( CMD4_ACC_TYPE_ENUM ).not.to.be.a( "null", "CMD4_ACC_TYPE_ENUM is null" );
71 | });
72 | it( "CMD4_ACC_TYPE_ENUM has EOL", ( ) =>
73 | {
74 | expect( CMD4_ACC_TYPE_ENUM.EOL ).not.to.be.a( "null", "CMD4_ACC_TYPE_ENUM.EOL is null" );
75 | });
76 | it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) =>
77 | {
78 | expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL, "CMD4_ACC_TYPE_ENUM.EOL. Expected: " + ACC_EOL + " found: " + CMD4_ACC_TYPE_ENUM.EOL );
79 | });
80 |
81 | it( "CMD4_ACC_TYPE_ENUM[ 0-" + CMD4_ACC_TYPE_ENUM.EOL + " ] to have a valid value", ( ) =>
82 | {
83 | for ( let index=0; index < CMD4_ACC_TYPE_ENUM.EOL; index ++ )
84 | {
85 | let keyIndex = getIndexOfValue( CMD4_ACC_TYPE_ENUM, index );
86 | expect( keyIndex ).to.equal( index, "Expected ACC ENUM at index: " + index + " to be: " + index + " found: " + keyIndex );
87 | }
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/test/isNumeric.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var isNumeric = require( "../utils/isNumeric.js" );
4 |
5 | describe( "Testing isNumeric", ( ) =>
6 | {
7 | it( "isNumeric should be a function", ( ) =>
8 | {
9 | assert.isFunction( isNumeric, "isNumeric is not a function" );
10 | });
11 |
12 | it( "isNumeric should correctly identify a string number", ( ) =>
13 | {
14 | let data = "12345";
15 | let expectedResult = true;
16 | let result = isNumeric( data );
17 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
18 | });
19 |
20 | it( "isNumeric should correctly identify a string float", ( ) =>
21 | {
22 | let data = "3.1415";
23 | let expectedResult = true;
24 | let result = isNumeric( data );
25 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
26 | });
27 |
28 | it( "isNumeric should correctly identify a number", ( ) =>
29 | {
30 | let data = 12345;
31 | let expectedResult = true;
32 | let result = isNumeric( data );
33 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
34 | });
35 |
36 | it( "isNumeric should correctly identify a float", ( ) =>
37 | {
38 | let data = 3.1415;
39 | let expectedResult = true;
40 | let result = isNumeric( data );
41 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
42 | });
43 |
44 | it( "isNumeric should correctly identify a negative float", ( ) =>
45 | {
46 | let data = -3.1415;
47 | let expectedResult = true;
48 | let result = isNumeric( data );
49 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
50 | });
51 |
52 | it( "isNumeric should correctly identify a 0", ( ) =>
53 | {
54 | let data = 0;
55 | let expectedResult = true;
56 | let result = isNumeric( data );
57 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
58 | });
59 |
60 | it( `isNumeric should correctly identify a "0"`, ( ) =>
61 | {
62 | let data = "0";
63 | let expectedResult = true;
64 | let result = isNumeric( data );
65 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
66 | });
67 |
68 |
69 |
70 | it( "isNumeric should correctly fail a character string", ( ) =>
71 | {
72 | let data = "One";
73 | let expectedResult = false;
74 | let result = isNumeric( data );
75 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
76 | });
77 |
78 | it( "isNumeric should correctly fail an undefined", ( ) =>
79 | {
80 | let data = undefined;
81 | let expectedResult = false;
82 | let result = isNumeric( data );
83 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
84 | });
85 | it( "isNumeric should correctly fail a null", ( ) =>
86 | {
87 | let data = null;
88 | let expectedResult = false;
89 | let result = isNumeric( data );
90 | assert.equal( result, expectedResult, "isNumeric( " + data + " ) returned: " + result + " expected: " + expectedResult );
91 | });
92 | })
93 |
94 |
--------------------------------------------------------------------------------
/test/indexOfEnum.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let { indexOfEnum } = require( "../utils/indexOfEnum" );
4 | Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } });
5 |
6 | // ***************** TEST Plugin Initialized Variables ***************
7 |
8 | describe( "Initializing our plugin module", ( ) => {});
9 |
10 | let _api = new HomebridgeAPI( ); // object we feed to Plugins
11 |
12 | // Init the library for all to use
13 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms );
14 | let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories );
15 |
16 |
17 | // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM *************
18 | describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) =>
19 | {
20 | it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) =>
21 | {
22 | expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL );
23 | });
24 | });
25 | describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) =>
26 | {
27 | it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) =>
28 | {
29 | expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL );
30 | });
31 | });
32 |
33 | // ******** TEST indexOfEnum .*************
34 |
35 | describe( "Test indexOfEnum import", ( ) =>
36 | {
37 | it( "indexOfEnum should be a function ", ( ) =>
38 | {
39 | assert.isFunction( indexOfEnum, "index of enum is not a function. Found: %s", typeof indexOfEnum );
40 | });
41 | });
42 |
43 | describe( "Test indexOfEnum", ( ) =>
44 | {
45 | it( "indexOfEnum should be identify a deviceName ", ( ) =>
46 | {
47 | let j = CMD4_DEVICE_TYPE_ENUM.WiFiSatellite;
48 | let name = CMD4_DEVICE_TYPE_ENUM.properties[ j ].deviceName;
49 | let ucKeyIndex = CMD4_DEVICE_TYPE_ENUM.properties.indexOfEnum( i => i.deviceName === name );
50 | assert.equal( j, ucKeyIndex, "index of enum should identify the device %s(%s). Found: %s", name, j, ucKeyIndex );
51 | });
52 |
53 | it( "indexOfEnum should identify a Characteristic Name ", ( ) =>
54 | {
55 | let j = CMD4_ACC_TYPE_ENUM.BatteryLevel;
56 | let type = CMD4_ACC_TYPE_ENUM.properties[ j ].type;
57 | let ucKeyIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === type);
58 | assert.equal( j, ucKeyIndex, "index of enum should identify the characteristic " + type + "(" + j + "). Found: " + ucKeyIndex );
59 | });
60 | it( "indexOfEnum should identify a Characteristic Name ", ( ) =>
61 | {
62 | let j = CMD4_ACC_TYPE_ENUM.CurrentRelativeHumidity;
63 | //let j = CMD4_ACC_TYPE_ENUM.CurrentHumidifierDehumidifierState;
64 | //let j = CMD4_ACC_TYPE_ENUM.blast;
65 | console.log("j=" + j);
66 | let type = CMD4_ACC_TYPE_ENUM.properties[ j ].type;
67 | let ucKeyIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === type);
68 | assert.equal( j, ucKeyIndex, "index of enum should identify the characteristic " + type + "(" + j + "). Found: " + ucKeyIndex );
69 | });
70 | describe( "Test each characteristic type", ( ) =>
71 | {
72 | for ( let index = 0; index < ACC_EOL; index++ )
73 | {
74 | it( "indexOfEnum should be identify a Characteristic Type ", ( ) =>
75 | {
76 | let type = CMD4_ACC_TYPE_ENUM.properties[ index ].type;
77 | let ucKeyIndex = CMD4_ACC_TYPE_ENUM.properties.indexOfEnum( i => i.type === type);
78 | assert.equal( index, ucKeyIndex, "index of enum should identify the characteristic " + type + "(" + index + "). Found: " + ucKeyIndex );
79 | });
80 | }
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/test/mocha-setup:
--------------------------------------------------------------------------------
1 | // Define common functions and values for all unit tests.
2 |
3 | const which = require('which');
4 | const path = require( "path" );
5 |
6 | // Get the real path of homebridge instead of a dev dependancy,
7 | // which caused issues if you forget to update dependancies but
8 | // upgrade homebridge.
9 | const homebridgePath = which.sync( 'homebridge', { nothrow: true } )
10 |
11 | let apiPath;
12 | if ( homebridgePath )
13 | {
14 | let dirname = path.dirname( homebridgePath );
15 |
16 | console.log( "Found homebridge in path %s", dirname );
17 | apiPath = `${ dirname }/../lib/node_modules/homebridge/lib/api`;
18 | global.HomebridgeAPI = require( apiPath ).HomebridgeAPI;
19 |
20 | if ( ! global.HomebridgeAPI )
21 | {
22 | console.log( "homebridgeAPI not available !!!" );
23 | process.exit( 10 );
24 | }
25 |
26 | // For serializing/deserializing arrays of accessories
27 | let platformAccessoryPath = `${ dirname }/../lib/node_modules/homebridge/lib/platformAccessory`;
28 | global.platformAccessory_1 = require( platformAccessoryPath );
29 |
30 | } else
31 | {
32 | console.log( "homebridge not found !!!" );
33 | process.exit( 10 );
34 | }
35 | console.log( "Found api in %s", apiPath );
36 |
37 | // IMPORTANT - ALL GLOBALS MUST BE DEFINED IN .eslintrc.json for lint to work
38 | global.fs = require( 'fs' );
39 | global.assert = require( "chai" ).assert;
40 | global.expect = require( "chai" ).expect;
41 | global.sinon = require( "sinon" );
42 |
43 | global.ACC_EOL = 255;
44 | global.DEVICE_EOL = 81;
45 | global.FORMAT_EOL = 11;
46 | global.UNITS_EOL = 5;
47 | global.PERMS_EOL = 9;
48 | global.ACCESS_EOL = 3;
49 |
50 | // These would be the uninitialized values, used for unit testing
51 | global.ACC_DATA = require( '../lib/CMD4_ACC_TYPE_ENUM' );
52 | global.CMD4_ACC_TYPE_ENUM = ACC_DATA.CMD4_ACC_TYPE_ENUM;
53 |
54 | global.DEVICE_DATA = require( '../lib/CMD4_DEVICE_TYPE_ENUM' );
55 | global.CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM;
56 |
57 | global.CHAR_DATA = require( `../lib/CMD4_CHAR_TYPE_ENUMS` );
58 | global.CMD4_CHAR_TYPE_ENUMS = CHAR_DATA.CMD4_CHAR_TYPE_ENUMS;
59 |
60 |
61 | global.Logger = require("../utils/Logger");
62 |
63 |
64 | // A true sleep ( blocking ).
65 | const moment = require( "moment" );
66 | global.sleep = function( secondsToSleep = 1 )
67 | {
68 | let sleepUntill = moment( ).add( secondsToSleep, 'seconds');
69 | while( moment ( ).isBefore( sleepUntill ) ) { /* block the process */ }
70 | }
71 |
72 | global.accEnumIndexToC = function( index )
73 | {
74 | return CMD4_ACC_TYPE_ENUM.properties[ index ].type;
75 | }
76 | global.devEnumIndexToC = function( index )
77 | {
78 | return CMD4_DEVICE_TYPE_ENUM.properties[ index ].deviceName;
79 | }
80 |
81 | // How it's used
82 | // sleep( 10 );
83 |
84 | global.cleanStatesDir = function( )
85 | {
86 | const os = require( "os" );
87 | const cmd4StateDir = os.homedir( ) + "/.homebridge/Cmd4Scripts/Cmd4States/"
88 |
89 | var glob = require( "glob" );
90 |
91 |
92 | glob( cmd4StateDir + "Status_Device_*", null, function ( er, files )
93 | {
94 | for ( var file of files )
95 | {
96 | // To use the promise of unlink, it must be in an async function
97 | // so put it in one. Why not unLinkSync, because for whatever reason
98 | // some files were notbremoved synchronously.
99 | ( async( ) =>
100 | {
101 | await fs.unlink( file, function( err, result )
102 | {
103 | if ( err && err.code != 'ENOENT' )
104 | console.log( 'file not removed err: ' + err + " result: " + result );
105 | });
106 | });
107 | }
108 | })
109 | }
110 |
111 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/PS5.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # This Cmd4 script uses playactor to turn on/off a PS5.
5 | #
6 | # You will need to install playactor and have the command in a global PATH
7 | # or modify this script with its actual PATH.
8 | #
9 | # Your Cmd4 .homebridge/.config.json file would have a state_cmd like:
10 | # state_cmd: ".homebridge/Cmd4Scripts/Examples/PS5.sh"
11 | #
12 | # Testing from the shell prompt:
13 | # ./PS5.sh Get PS5 On
14 | # or
15 | # ./PS5.sh Set PS5 On 1
16 | # or
17 | # ./PS5.sh Set PS5 On 0
18 |
19 |
20 | set -e
21 |
22 | # Exit immediately for unbound variables.
23 | set -u
24 |
25 | # Passed in Args
26 | length=$#
27 | device=""
28 | io=""
29 | characteristic=""
30 |
31 |
32 | if [ $length -le 2 ]; then
33 | echo "Usage: $0 Get < AccessoryName > < characteristic >"
34 | echo "Usage: $0 Set < AccessoryName > < characteristic > < Value >"
35 | exit 199
36 | fi
37 |
38 |
39 | if [ $length -ge 1 ]; then
40 | io=$1
41 | fi
42 | if [ $length -ge 2 ]; then
43 | device=$2
44 | fi
45 | if [ $length -ge 3 ]; then
46 | characteristic=$3
47 | fi
48 |
49 |
50 | # For "Get" Directives
51 | if [ "$io" = "Get" ]; then
52 | case "$characteristic" in
53 |
54 | On )
55 |
56 | # Normally we would exit immediately if a command fails with a non-zero status.
57 | # In this case playactor can fail and we would rely on the failing exit status to
58 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
59 | # thing to do. However for this example we are going to output '0' (false) so
60 | # that you can see the '0' on the console telling us that the accessory is not
61 | # on the network.
62 | set +e
63 |
64 | # Check if we got the message '200 OK' meaning the accessory is
65 | # on the network by seeing if the return code of the above command passed or
66 | # failed.
67 | playactor check | grep -i '200 Ok'>> /dev/null 2>&1
68 | rc=$?
69 | set -e
70 |
71 | if [ "$rc" = "0" ]; then
72 | # The message was recieved so the target is up, sending a '1' (true), like
73 | # a binary number is, back to Cmd4.
74 | stdbuf -o0 -e0 echo 1
75 | exit 0
76 | else
77 | # The message was not recieved so the target must be down, sending a '0' (false), like
78 | # a binary number is, back to Cmd4.
79 | stdbuf -o0 -e0 echo 0
80 | exit 0
81 | fi
82 | ;;
83 | *)
84 | echo "Unhandled Get characteristic $characteristic" >&2
85 | exit 109
86 | ;;
87 | esac
88 | fi
89 |
90 | # For "Set" Directives
91 | if [ "$io" = "Set" ]; then
92 | value="1"
93 | if [ $length -ge 4 ]; then
94 | value=$4
95 | else
96 | echo "No value specified for set" >&2
97 | exit 199
98 | fi
99 |
100 | case "$characteristic" in
101 | On )
102 | # Normally we would exit immediately if a command fails with a non-zero status.
103 | # In this case playactor can fail and we would rely on the failing exit status to
104 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
105 | # thing to do. However for this example we are going to output '0' (false) so
106 | # that you can see the '0' on the console telling us that the accessory is not
107 | # on the network.
108 | set +e
109 |
110 | if [ "$value" = "1" ]; then
111 |
112 | # Execute the on command
113 | sudo playactor wake
114 |
115 | exit 0
116 | else
117 | # Execute the off command
118 | sudo playactor standby
119 |
120 | exit 0
121 | fi
122 | ;;
123 | *)
124 | echo "Unhandled Set characteristic $characteristic" >&2
125 | exit 109
126 | ;;
127 | esac
128 | fi
129 |
130 |
131 | echo "Unhandled $io $device $characteristic" >&2
132 |
133 | exit 150
134 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/ExampleShellScript_template.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # ExampleScript_template.sh
4 | #
5 | # Description:
6 | # This script is a goood starting place for you to create Cmd4 Scripts
7 | # of your own
8 | #
9 | # Parameters are:
10 | # Get < Any accessory name > < Characteristic>
11 | # Set < Any accessory name > < Characteristic> < value >
12 | #
13 | # Note 1: These paramaters match the those of the Cmd4 plugin.
14 | # A full lost of supported devices and characteristics can be
15 | # found at:
16 | # https://ztalbot2000.github.io/homebridge-cmd4
17 | #
18 | # How it works:
19 | #
20 | # The Cmd4 plugin will call this script to retrieve those states
21 | # you have defined as not Cached to Get/Set your devices characteristic
22 | # states.
23 | #
24 | # For example:
25 | # bash ExampleScript_template.sh Set My_Door TargetDoorState 0
26 | # or
27 | # bash ExampleScript.sh Get My_Door CurrentDoorState
28 | #
29 |
30 | set -e
31 |
32 | # Exit immediately for unbound variables.
33 | set -u
34 |
35 |
36 | length=$#
37 | device=""
38 | io=""
39 | characteristic=""
40 | option=""
41 |
42 | if [ $length -le 1 ]; then
43 | printf "Usage: $0 Get < AccessoryName > < Characteristic >\n"
44 | printf "Usage: $0 Set < AccessoryName > < Characteristic > < Value >\n"
45 | exit -1
46 | fi
47 |
48 | # printf "args =$#\n" # debug
49 | # printf "arg1 =$1\n" # debug
50 |
51 | if [ $length -ge 1 ]; then
52 | io=$1
53 | # printf "io=$io\n" # debug
54 | fi
55 | if [ $length -ge 2 ]; then
56 | device=$2
57 | # printf "device = ${device}\n" # debug
58 | fi
59 | if [ $length -ge 3 ]; then
60 | characteristic=$3
61 | # printf "Characteristic = ${characteristic}\n" # debug
62 | fi
63 | if [ $length -ge 4 ]; then
64 | option=$4
65 | # printf "option = ${option}\n" # debug
66 | fi
67 |
68 | if [ "${io}" == "Get" ]; then
69 | case $characteristic in
70 | 'CurrentDoorState')
71 |
72 | printf "0\n" # Door is open
73 |
74 | # See https://ztalbot2000.github.io/homebridge-cmd4
75 | # For the possible values and characteristics
76 | # available per device. It will show somethink like:
77 | # Valid Values:
78 | # 0 - "Open. The door is fully open."
79 | # 1 - "Closed. The door is fully closed."
80 | # 2 - "Opening. The door is actively opening."
81 | # 3 - "Closing. The door is actively closing."
82 | # 4 - "Stopped. The door is not moving, and it is not fully
83 | # open nor fully closed."
84 | # 5-255 - "Reserved"
85 | exit 0
86 | ;;
87 | 'TargetDoorState')
88 | printf "0\n"
89 | exit 0
90 | ;;
91 | 'ObstructionDetected')
92 | printf "0\n"
93 | exit 0
94 | ;;
95 | 'LockCurrentState')
96 | printf "0\n"
97 | exit 0
98 | ;;
99 | *)
100 | printf "UnHandled Get ${device} Characteristic ${characteristic}\n"
101 | exit -1
102 | ;;
103 | esac
104 | fi
105 | if [ "${io}" == 'Set' ]; then
106 | case $characteristic in
107 | 'CurrentDoorState')
108 | # Current Door State is not settable. The
109 | # call would be to TargetDoorState. This is here
110 | # for debugging only.
111 |
112 | exit 0
113 | ;;
114 | 'TargetDoorState')
115 | # Do something of your own here.
116 | exit 0
117 | ;;
118 | 'ObstructionDetected')
119 | # Obstruction Detected is not settable. It
120 | # call is a read-only characteristic. This is here
121 | # for debugging only.
122 | exit 0
123 | ;;
124 | 'LockCurrentState')
125 | # Lock Current State is not settable. It
126 | # call is a read-only characteristic. This is here
127 | # for debugging only.
128 | exit 0
129 | ;;
130 | *)
131 | printf "UnHandled Set GarageDoorOpenner Characteristic ${characteristic}"
132 | exit -1
133 | ;;
134 | esac
135 | fi
136 | printf "Unknown io command ${io}\n"
137 | exit -1
138 |
139 |
140 |
--------------------------------------------------------------------------------
/lib/CMD4_CHAR_TYPE_ENUMS.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var CMD4_CHAR_TYPE_ENUMS =
4 | {
5 | CMD4_FORMAT_TYPE_ENUM:
6 | {
7 | BOOL: 0,
8 | INT: 1,
9 | FLOAT: 2,
10 | STRING: 3,
11 | UINT8: 4,
12 | UINT16: 5,
13 | UINT32: 6,
14 | UINT64: 7,
15 | DATA: 8,
16 | TLV8: 9,
17 | DICTIONARY: 10,
18 | EOL: 11,
19 | properties: { }
20 | },
21 |
22 | CMD4_UNITS_TYPE_ENUM:
23 | {
24 | CELSIUS: 0,
25 | PERCENTAGE: 1,
26 | ARC_DEGREE: 2,
27 | LUX: 3,
28 | SECONDS: 4,
29 | EOL: 5,
30 |
31 | properties: { }
32 | },
33 |
34 | CMD4_PERMS_TYPE_ENUM:
35 | {
36 | READ: 0,
37 | WRITE: 1,
38 | PAIRED_READ: 2,
39 | PAIRED_WRITE: 3,
40 | NOTIFY: 4,
41 | ADDITIONAL_AUTHORIZATION: 5,
42 | TIMED_WRITE: 6,
43 | HIDDEN: 7,
44 | WRITE_RESPONSE: 8,
45 | EOL: 9,
46 | properties: { }
47 | }
48 | }
49 |
50 | // Export both the init function and the uninitialized data for unit testing
51 | module.exports =
52 | {
53 | init: function ( hapFormats, hapUnits, hapPerms )
54 | {
55 |
56 | // Fill in the properties of all possible characteristics
57 | // props was added because calling getCharacteridtic().props.perms adds
58 | // the characteristic in by default. This costs some lines, but is advantageous.
59 | CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.properties =
60 | {
61 | 0: { type: hapFormats.BOOL // "bool"
62 | },
63 | 1: { type: hapFormats.INT // "int"
64 | },
65 | 2: { type: hapFormats.FLOAT // "float"
66 | },
67 | 3: { type: hapFormats.STRING // "string"
68 | },
69 | 4: { type: hapFormats.UINT8 // "uint8"
70 | },
71 | 5: { type: hapFormats.UINT16 // "uint16"
72 | },
73 | 6: { type: hapFormats.UINT32 // "uint32"
74 | },
75 | 7: { type: hapFormats.UINT64 // "uint64"
76 | },
77 | 8: { type: hapFormats.DATA // "data"
78 | },
79 | 9: { type: hapFormats.TLV8 // "tlv8"
80 | },
81 | 10: { type: hapFormats.ARRAY // "array"
82 | },
83 | 11: { type: hapFormats.DICTIONARY // "dict"
84 | }
85 | };
86 |
87 | CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.properties =
88 | {
89 | 0: { type: hapUnits.CELSIUS // "celsius"
90 | },
91 | 1: { type: hapUnits.PERCENTAGE // "percentage"
92 | },
93 | 2: { type: hapUnits.ARC_DEGREE // "arcdegrees"
94 | },
95 | 3: { type: hapUnits.LUX // "lux"
96 | },
97 | 4: { type: hapUnits.SECONDS // "seconds"
98 | }
99 | };
100 |
101 | CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.properties =
102 | {
103 | 0: { type: hapPerms.READ // "pr"
104 | },
105 | 1: { type: hapPerms.WRITE // "pw"
106 | },
107 | 2: { type: hapPerms.PAIRED_READ // "pr"
108 | },
109 | 3: { type: hapPerms.PAIRED_WRITE // "pw"
110 | },
111 | 4: { type: hapPerms.NOTIFY // "ev"
112 | },
113 | 5: { type: hapPerms.EVENTS // "ev"
114 | },
115 | 6: { type: hapPerms.ADDITIONAL_AUTHORIZATION // "aa"
116 | },
117 | 7: { type: hapPerms.TIMED_WRITE // "tw"
118 | },
119 | 8: { type: hapPerms.HIDDEN // "hd"
120 | },
121 | 9: { type: hapPerms.WRITE_RESPONSE // "wr"
122 | }
123 | };
124 |
125 | return CMD4_CHAR_TYPE_ENUMS;
126 |
127 | }, CMD4_CHAR_TYPE_ENUMS
128 | }
129 |
130 |
--------------------------------------------------------------------------------
/utils/transposeCMD4Props.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Description:
4 | // Routines to convert Cmd4 values to constants and back.
5 | //
6 | // @param CMD4_ACC_TYPE_ENUM - Just that
7 | // @param accTypeEnumIndex - The Accessory Type Enumerated index.
8 | // @param constantString - The string to change into a HAP value.
9 | // @param constantValue - The value to change into a HAP String.
10 | //
11 | // @returns Value of transposition or nothing.
12 | //
13 |
14 | var extractKeyValue = function( obj, value )
15 | {
16 | for ( let key in obj )
17 | {
18 | // In case value given is a string, compare that as well.
19 | if ( obj[ key ] == value || obj[ key ] + "" == value )
20 | return key;
21 | }
22 | return undefined;
23 | }
24 |
25 | // Used to convet ValidValus from a Constant to their corresponding value.
26 | var transposeConstantToValidValue = function ( CMD4_ENUM_properties_obj, accTypeEnumIndex, constantString )
27 | {
28 | if ( Object.keys( CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues ).length <= 0 )
29 | {
30 | // Return the original as it should be used instead of nothing
31 | // This is not a failure
32 | //return { "value": constantString, "rc": true, "msg": `Non Convertible characteristic ${ constantString } for ${ CMD4_ENUM_properties_obj[ accTypeEnumIndex ].type }` };
33 | return constantString;
34 | }
35 |
36 | // In case constantString is not a string, ie false
37 | let lookupString = "" + constantString;
38 | let ucConstantString = lookupString.toUpperCase();
39 |
40 | if ( Object.prototype.hasOwnProperty.call( CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues, ucConstantString ) )
41 | {
42 | // let value = CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues[ ucConstantString ];
43 | return CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues[ ucConstantString ];
44 |
45 | //return { "value": value, "rc": true, "msg": "Transpose success" };
46 | }
47 |
48 | // What if it is already transposed correctly?
49 | // let constant = extractKeyValue( CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues, constantString );
50 | // if ( constant == undefined || constant == null )
51 | // return { "value": constantString, "rc": false, "msg": `Cannot convert ${ constantString } to a value for ${ CMD4_ENUM_properties_obj[ accTypeEnumIndex ].type }` };
52 | //else
53 | // return { "value": constantString, "rc": true, "msg": "Already transposed" };
54 | return constantString;
55 | }
56 |
57 | // Used to convet ValidValues Value to its corresponding Constant.
58 | var transposeValueToValidConstant = function ( CMD4_ENUM_properties_obj, accTypeEnumIndex, valueString )
59 | {
60 | if ( Object.keys( CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues ).length <= 0)
61 | {
62 | // Return the original as it should be used instead of nothing
63 | // This is not a failure
64 | //return { "value": valueString, "rc": true, "msg": `Non Convertible characteristic ${ valueString } for ${ CMD4_ENUM_properties_obj[ accTypeEnumIndex ].type }` };
65 | return valueString;
66 | }
67 |
68 | let constant = extractKeyValue( CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues, valueString );
69 |
70 | if ( constant == undefined || constant == null )
71 | {
72 | // What if it is already transposed correctly?
73 | //let value = CMD4_ENUM_properties_obj[ accTypeEnumIndex ].validValues[ valueString ];
74 | //if ( value == undefined || value == null )
75 | // return { "value": valueString, "rc": false, "msg": `Cannot convert ${ valueString } to a constant for ${ CMD4_ENUM_properties_obj[ accTypeEnumIndex ].type }` };
76 | //else
77 | // return { "value": valueString, "rc": true, "msg": "Already transposed" };
78 |
79 | return valueString;
80 | }
81 |
82 | // return { "value": constant, "rc": true, "msg": "Transpose success" };
83 | return constant;
84 | }
85 |
86 | // SendValue does not send true/false for historical reasons
87 | var transposeBoolToValue = function ( valueString )
88 | {
89 | if ( valueString == true )
90 | return 1;
91 | if ( valueString == false )
92 | return 0;
93 |
94 | return valueString;
95 | }
96 |
97 | module.exports = {
98 | transposeConstantToValidValue,
99 | transposeValueToValidConstant,
100 | transposeBoolToValue,
101 | extractKeyValue
102 | };
103 |
--------------------------------------------------------------------------------
/tools/whereIsConstant:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | "use strict";
3 |
4 | // File System utilities
5 | let fs = require("fs");
6 |
7 | // Command line parser
8 | const { Command } = require( "commander" );
9 | const program = new Command;
10 |
11 | const constants = require( "../cmd4Constants" );
12 |
13 | var cmd4Files = [ "./Cmd4Platform.js",
14 | "./Cmd4Accessory.js",
15 | "./Cmd4PriorityPollingQueue.js",
16 | "./utils/HV.js",
17 | "./index.js",
18 | "./lib/CMD4_DEVICE_TYPE_ENUM.js",
19 | "./tools/Cmd4AccDocGenerator"];
20 |
21 | // A nice little getOpt node.js package
22 | program
23 | .description( 'Determine where a given constant is in our source.' )
24 | .requiredOption( '-c, --constant ', 'Constant to search for>' )
25 | .option( '-r, --reverse', 'Look for key of constant' )
26 | .option( '-v, --verbose', 'Show verbose search results' );
27 |
28 | // Parse the arguments passed into this program.
29 | program.parse( process.argv );
30 |
31 |
32 | // Get the options passed in based on the commander getOpts definitions.
33 | let options = program.opts( );
34 |
35 | // This is the count of the found constants in result
36 | let foundCount=0;
37 |
38 | // Stop after finding this many
39 | const MAX_FOUND_BEFORE_EXIT = 3
40 |
41 | // The regex to find the constant in the source files
42 | // A great regex site:
43 | // https://regex101.com/r/bE3c0x/5
44 | let reg;
45 |
46 | let lookingFor = "";
47 |
48 | if ( options.reverse )
49 | {
50 | // WORKS. Ex: whereIsConstant -r -v -c PUBLISHEXTERNALLY
51 | lookingFor = constants[ options.constant ];
52 | if ( lookingFor == undefined )
53 | {
54 | console.error( `Constant key: ${ options.constant } not defined in cmd4Constants.js` );
55 | process.exit( 1 );
56 | }
57 | reg = `.*constants.${ options.constant }.*$`;
58 | // My logic seems reversed, but that is not true
59 | console.log( `Looking for: constants.${ options.constant }` );
60 | if ( options.verbose )
61 | console.log( `using regex: ${ reg }` );
62 | } else
63 | {
64 | // Works. Ex: whereIsConstant -r -v -c publishExternally
65 | lookingFor = Object.keys( constants ).find(key => constants[ key ] === options.constant );
66 | if ( lookingFor == undefined )
67 | {
68 | console.error( `Constant value: ${ options.constant } not defined in cmd4Constants.js` );
69 | process.exit( 1 );
70 | }
71 | reg = `\\w*.${ options.constant }.*$`;
72 | // My logic seems reversed, but that is not true
73 | console.log( `Looking for: .${ lookingFor }` );
74 | if ( options.verbose )
75 | console.log( `using regex: ${ reg }` );
76 | }
77 | const regex = new RegExp( reg );
78 |
79 |
80 | // The constant must be in one of the Cmd4 source files
81 | for ( let fileIndex = 0;
82 | (fileIndex < cmd4Files.length );
83 | fileIndex++ )
84 | {
85 | let cmd4File = cmd4Files[ fileIndex ];
86 | if ( options.verbose )
87 | console.log( `Checking file: ${ cmd4File }` );
88 |
89 | // Read in all the code from the source file
90 | let code = fs.readFileSync( cmd4File, "utf8" );
91 |
92 | // If I could grep the source file I would, so
93 | // check the regex against each line
94 | var codeLines = code.split( '\n' );
95 | let lineCount = 0;
96 | for ( let lineIndex = 0;
97 | lineIndex < codeLines.length;
98 | lineIndex++, lineCount++ )
99 | {
100 | let line = codeLines[ lineIndex ];
101 |
102 | // Check the regex
103 | let t = regex.test( line );
104 | if ( t == true )
105 | {
106 | foundCount++;
107 | if ( foundCount == 1 )
108 | {
109 | process.stdout.write( `Found in ${ cmd4File }` );
110 | }
111 | else if ( foundCount <= MAX_FOUND_BEFORE_EXIT )
112 | {
113 | process.stdout.write( `, ${ cmd4File }` );
114 | }
115 | else
116 | {
117 | process.stdout.write(` ...\n` );
118 | process.exit( 0 );
119 | }
120 | }
121 | }
122 | if (foundCount == MAX_FOUND_BEFORE_EXIT )
123 | break;
124 | }
125 | if (foundCount == 0 )
126 | {
127 | console.log( `constant: ${ options.constant } not found in Cmd4 source files` );
128 | process.exit( 1 );
129 | } else
130 | {
131 | // Trailing \n
132 | console.log("");
133 | }
134 | process.exit( 0 );
135 |
136 |
--------------------------------------------------------------------------------
/test/trueTypeOf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var trueTypeOf = require( "../utils/trueTypeOf.js" );
4 |
5 | describe( "Testing trueTypeOf", ( ) =>
6 | {
7 | it( "trueTypeOf should be a function", ( ) =>
8 | {
9 | assert.isFunction( trueTypeOf, "trueTypeOf is not a function" );
10 | });
11 |
12 | it( "trueTypeOf should correctly identify a String object", ( ) =>
13 | {
14 | let data = "Cmd4";
15 | let expectedResult = String;
16 | let result = trueTypeOf( data );
17 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
18 | });
19 |
20 | it( "trueTypeOf should correctly identify a Boolean false object", ( ) =>
21 | {
22 | let data = false;
23 | let expectedResult = Boolean;
24 | let result = trueTypeOf( data );
25 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
26 | });
27 |
28 | it( "trueTypeOf should correctly identify a Boolean true object", ( ) =>
29 | {
30 | let data = true;
31 | let expectedResult = Boolean;
32 | let result = trueTypeOf( data );
33 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
34 | });
35 |
36 | it( "trueTypeOf should correctly identify a Number object", ( ) =>
37 | {
38 | let data = 42;
39 | let expectedResult = Number;
40 | let result = trueTypeOf( data );
41 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
42 | });
43 |
44 | it( "trueTypeOf should correctly identify a Array object", ( ) =>
45 | {
46 | let data = [ 1, 2, 3 ];
47 | let expectedResult = Array;
48 | let result = trueTypeOf( data );
49 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
50 | });
51 |
52 | it( "trueTypeOf should correctly identify a polling config", ( ) =>
53 | {
54 | let data = [ { "characteristic": "active", "timeout": 5, "interval": 3},
55 | { "characteristic": "On", "timeout": 8, "interval": 4}
56 | ];
57 | let expectedResult = Array;
58 | let result = trueTypeOf( data );
59 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
60 | });
61 |
62 | it( "trueTypeOf should correctly identify an empty object ", ( ) =>
63 | {
64 | let data = undefined
65 | let expectedResult = undefined;
66 | let result = trueTypeOf( );
67 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
68 | });
69 |
70 | it( "trueTypeOf should correctly identify a undefined object ", ( ) =>
71 | {
72 | let data = undefined
73 | let expectedResult = undefined;
74 | let result = trueTypeOf( data );
75 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
76 | });
77 |
78 | it( "trueTypeOf should correctly identify a null object", ( ) =>
79 | {
80 | let data = null;
81 | let expectedResult = null;
82 | let result = trueTypeOf( data );
83 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
84 | });
85 |
86 | it( "trueTypeOf should correctly identify a 0 as a Number", ( ) =>
87 | {
88 | let data = 0;
89 | let expectedResult = Number;
90 | let result = trueTypeOf( data );
91 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
92 | });
93 |
94 | it( "trueTypeOf should correctly identify a \"0\" as a String", ( ) =>
95 | {
96 | let data = "0";
97 | let expectedResult = String;
98 | let result = trueTypeOf( data );
99 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
100 | });
101 |
102 | it( "trueTypeOf should correctly identify a \"1\" as a String", ( ) =>
103 | {
104 | let data = "0";
105 | let expectedResult = String;
106 | let result = trueTypeOf( data );
107 | assert.equal( result, expectedResult, "trueTypeOf( " + data + " ) returned: " + result + " expected: " + expectedResult );
108 | });
109 | })
110 |
111 |
--------------------------------------------------------------------------------
/test/Cmd4Mode.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // ***************** TEST LOADING **********************
4 |
5 |
6 | let { Cmd4Platform } = require( "../Cmd4Platform" );
7 | let { Cmd4Accessory } = require( "../Cmd4Accessory" );
8 |
9 | // Settings, Globals and Constants
10 | let settings = require( "../cmd4Settings" );
11 |
12 |
13 | var _api = new HomebridgeAPI( ); // object we feed to Plugins
14 |
15 |
16 | // Init the library for all to use
17 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms );
18 | let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories );
19 |
20 |
21 |
22 | // ******** QUICK TEST CMD4_ACC_TYPE_ENUM *************
23 | describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) =>
24 | {
25 | it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) =>
26 | {
27 | expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL );
28 | });
29 | });
30 |
31 |
32 |
33 | // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM *************
34 | describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) =>
35 | {
36 | it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) =>
37 | {
38 | expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL );
39 | });
40 | });
41 |
42 | describe( "Testing Demo Mode", function( )
43 | {
44 |
45 | beforeEach( function( )
46 | {
47 | settings.listOfCreatedPriorityQueues = { };
48 | });
49 | afterEach( function( )
50 | {
51 | // Clear any timers created for any polling queue
52 | Object.keys(settings.listOfCreatedPriorityQueues).forEach( (queueName) =>
53 | {
54 | let queue = settings.listOfCreatedPriorityQueues[ queueName ];
55 | Object.keys(queue.listOfRunningPolls).forEach( (key) =>
56 | {
57 | let timer = queue.listOfRunningPolls[ key ];
58 | clearTimeout( timer );
59 | });
60 |
61 | clearTimeout( queue.pauseTimer );
62 | });
63 |
64 | // Put back the polling queues
65 | settings.listOfCreatedPriorityQueues = { };
66 | });
67 |
68 |
69 | it( "Test if Cmd4Accessory exists", function ( )
70 | {
71 | expect( Cmd4Accessory ).not.to.be.a( "null", "Cmd4Accessory was null" );
72 | });
73 |
74 | it( "V2 Crippled Test that getValue (Cached) occurs in Demo mode", function( done )
75 | {
76 | let platformConfig =
77 | {
78 | accessories:
79 | [{
80 | name: "MySwitch",
81 | type: "Switch",
82 | on: false,
83 | }]
84 | };
85 |
86 | const log = new Logger( );
87 | log.setBufferEnabled( );
88 | log.setOutputEnabled( false );
89 | log.setDebugEnabled( true );
90 |
91 | let cmd4Platform = new Cmd4Platform( log, platformConfig, _api );
92 |
93 | expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" );
94 |
95 | cmd4Platform.discoverDevices( );
96 |
97 | assert.include( log.logBuf, `[35mConfiguring platformAccessory: \u001b[39mMySwitch`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` );
98 | assert.include( log.logBuf, `[33mAdding getCachedValue for MySwitch characteristic: On`, ` cmd4Accessory incorrect stdout": ${ log.logBuf }` );
99 |
100 | let cmd4Accessory = cmd4Platform.createdCmd4Accessories[0];
101 |
102 | expect( cmd4Accessory ).to.be.a.instanceOf( Cmd4Accessory, "Cmd4Accessory is not an instance of Cmd4Accessory" );
103 |
104 | //log.reset( );
105 | log.setOutputEnabled( false );
106 | log.setDebugEnabled( true );
107 |
108 | /* Characteristic.getValue() is deprecated in V2 and Characteristic.value does not call the
109 | * get functions. Worked last in homebridge-1.8
110 | // Call the getValue bound function, which is priorityGetValue
111 | cmd4Accessory.service.getCharacteristic(
112 | CMD4_ACC_TYPE_ENUM.properties[ CMD4_ACC_TYPE_ENUM.On ]
113 | .characteristic ).getValue( "On", function dummyCallback( ) { } );
114 |
115 | setTimeout( ( ) =>
116 | {
117 | assert.include( log.logBuf, `[90mgetCachedValue On for: MySwitch returned (CACHED) value: false`, ` getValue incorrect stdout: ${ log.logBuf }` );
118 | assert.equal( log.errBuf, "", ` getValue Unexpected stderr: ${ log.errBuf }` );
119 |
120 | done( );
121 | }, 1000 );
122 |
123 | */
124 | // Added for homebridge v2 .getValue lines commented out above.
125 | done();
126 | }).timeout( 2000 );
127 |
128 | });
129 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/CheckYourScript.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash --noprofile --norc
2 |
3 | # Fun colour & cursor stuff
4 | TCR=$(tput cr)
5 | TCLR=$(tput clear)
6 | TBLD=$(tput bold)
7 | TNRM=$(tput sgr0)
8 | TBLK=$(tput setaf 0)
9 | TRED=$(tput setaf 1)
10 | TGRN=$(tput setaf 2)
11 | TYEL=$(tput setaf 3)
12 | TBLU=$(tput setaf 4)
13 | TMAG=$(tput setaf 5)
14 | TCYN=$(tput setaf 6)
15 | TWHT=$(tput setaf 7)
16 |
17 | # OS type for flavours of different commands (like date on OSX)
18 | case $(uname | tr '[:upper:]' '[:lower:]') in
19 | solaris*)
20 | date_cmd="date -u +%s.%N"
21 | ;;
22 | darwin*)
23 | # OSX does not have msec
24 | date_cmd="date -u +%s"
25 | ;;
26 | linux*)
27 | date_cmd="date -u +%s.%N"
28 | ;;
29 | bsd*)
30 | date_cmd="date -u +%s.%N"
31 | ;;
32 | msys*)
33 | date_cmd="date -u +%s.%N"
34 | ;;
35 | *)
36 | echo "unknown: OSTYPE:$OSTYPE"
37 | exit -1
38 | ;;
39 | esac
40 |
41 | printf "${TCLR}"
42 |
43 | if [ "${1}" = '-h' ]; then
44 | printf "${TBLU}Usage:${TNRM}"
45 | printf "${TNRM} SHELL> cd \n"
46 | printf "${TNRM} SHELL> bash --noprofile --norc \n"
47 | printf "\n"
48 | printf "${TBLU}Syntax:${TNRM}\n"
49 | printf " ${TBLU}${0}${TNRM} 'full state command'\n"
50 | printf "${TBLU}i.e.${TNRM}\n"
51 | printf " '.homebridge/Cmd4Scripts/CheckYourScript.sh' 'bin/MyExec' 'Get' 'MyDevice' 'On'\n"
52 | printf " or\n"
53 | printf " '.homebridge/Cmd4Scripts/CheckYourScript.sh' 'bash bin/YourScript.sh' 'Get' 'MyDevice' 'On'\n"
54 | printf " or\n"
55 | printf " '.homebridge/Cmd4Scripts/CheckYourScript.sh' 'bin/YourScript.sh 'Get 'MyDevice' 'On'\n"
56 | printf "\n"
57 | printf "Note: Add the '' around the command to prevent globbing, which is not done by homebridge-cmd4\n"
58 | exit 0
59 | fi
60 |
61 | # Processes are run from your home directory, so go there first.
62 | cd "${HOME}"
63 | printf "${TBLU}Changing to:${TNRM}'${HOME}' ${TBLU}where processes are run from${TNRM}\n"
64 |
65 | # $HOME is expanded by the shell and not scripts, so do not rely on it.
66 | unset HOME
67 |
68 | printf "${TBLU}Enviroment in shell is limited to these variables:${TNRM}\n"
69 | env
70 |
71 | printf "\n"
72 |
73 | printf "${TBLU}Command will be run from the directory: ${TNRM}${PWD}\n"
74 |
75 | output=""
76 | rc=0
77 |
78 | if [ "$2" = 'Set' ] || [ "$3" = 'Set' ]; then
79 | printf "${TBLU}(Set) Cmd4 would execute:${TNRM} $* "
80 | printf "\n"
81 |
82 | start_time="$( $date_cmd )"
83 | output=$("$@")
84 | rc="$?"
85 | end_time="$( $date_cmd )"
86 |
87 | # The elapsed time (in microseconds)
88 | # We add 1 second as OSX does not have msec dates.
89 | # One second will make no difference, but might help a newbie.
90 | elapsed=( $(/usr/bin/bc <<<"($end_time-$start_time) * 1000 + 1000") )
91 |
92 | printf "\n"
93 | if [ $rc = 0 ]; then
94 | printf "${TGRN}Command passed with returned code:${TNRM}'${rc}'\n"
95 | if [ "${output}" != "" ]; then
96 | printf "Output: '${output}' would be ignored\n"
97 | fi
98 | printf "$TBLU}The timeout value should be at least :${TNRM} ${elapsed} (microseconds)\n"
99 | printf "$TBLU}Multiply by 5 for safety.${TNRM}\n"
100 | else
101 | printf "${TRED}Command given did not exit with a ${TNRM}0${TRED} return code and would fail in homebridge-cmd4 rc=${TNRM}'${rc}'\n"
102 | printf "Understand that the error given is a result of running your command in a basic shell environment. Head the errors given\n"
103 | fi
104 | else
105 | printf "${TBLU}(Get) Cmd4 would execute:${TNRM} $*"
106 | printf "\n"
107 |
108 | start_time="$( $date_cmd )"
109 | output=$("$@")
110 | rc="$?"
111 | end_time="$( $date_cmd )"
112 |
113 | # The elapsed time (in microseconds)
114 | # We add 1 second as OSX does not have msec dates.
115 | elapsed=( $(/usr/bin/bc <<<"($end_time-$start_time) * 1000 + 1000") )
116 |
117 | printf "\n"
118 | wordCount=0
119 | if [ $rc = 0 ]; then
120 | printf "${TGRN}Command passed with returned code:${TNRM}'${rc}'\n"
121 | printf "${TBLU}Output: of command was:${TNRM}'${output}'\n"
122 | wordCount=$(IFS=' '; set -f; set -- "${output}"; echo $#)
123 | if [ "${wordCount}" != '1' ]; then
124 | printf "${TYEL}Word count of output should only be 1, not ${TNRM}'${wordCount}'\n"
125 | fi
126 | printf "$TBLU}The timeout value should be at least :${TNRM} ${elapsed} (microseconds)\n"
127 | printf "$TBLU}Multiply by 5 for safety.${TNRM}\n"
128 | else
129 | printf "${TRED}Command given did not exit with a ${TNRM}0${TRED} return code and would fail in homebridge-cmd4 rc=${TNRM}'${rc}'\n"
130 | printf "Understand that the error given is a result of running your command in a basic shell environment. Head the errors given\n"
131 | fi
132 | fi
133 |
134 |
135 | exit ${rc}
136 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/PS4.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | #
5 | # This Cmd4 example demonstrates a script that is compatible with
6 | # cmdSwitch2's example of controlling a PS4.
7 | #
8 | # Your Cmd4 .homebridge/.config.json file would have a state_cmd like:
9 | # state_cmd: ".homebridge/Cmd4Scripts/Examples/PS4.sh"
10 | #
11 | # Testing from the shell prompt:
12 | # ./PS4.sh Get PS4 On
13 | # or
14 | # ./PS4.sh Set PS4 On 1
15 | # or
16 | # ./PS4.sh Set PS4 On 0
17 |
18 | # Exit immediately if a command exits with a non-zero status
19 | set -e
20 |
21 | # Check if the first parameter to this script was "Get" for getting an accessory's
22 | # specific attribute.
23 | if [ "$1" = "Get" ]; then
24 |
25 | # Normally we would exit immediately if a command fails with a non-zero status.
26 | # In this case ps4-waker can fail and we would rely on the failing exit status to
27 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
28 | # thing to do. However for this example we are going to output '0' (false) so
29 | # that you can see the '0' on the console telling us that the accessory is not
30 | # on the network.
31 | set +e
32 |
33 | ps4-waker search | grep -i '200 Ok' >> /dev/null 2>&1
34 | rc=$?
35 |
36 | # Exit immediately if a command exits with a non-zero status
37 | set -e
38 |
39 | # Check if we got the message '200 OK' meaning the accessory is
40 | # on the network by seeing if the return code of the above command passed or
41 | # failed.
42 | if [ "$rc" = "0" ]; then
43 | # The message was recieved so the target is up, sending a '1' (true), like
44 | # a binary number is, back to Cmd4.
45 | echo "1"
46 |
47 | # Exit this script positivitely.
48 | exit 0
49 | else
50 | # The message was not recieved so the target must be down, sending a '0' (false), like
51 | # a binary number is, back to Cmd4.
52 | echo "0"
53 |
54 | # Exit this script positivitely, even though ps4-waker failed.
55 | exit 0
56 | fi
57 | fi
58 |
59 | # Check if the first parameter to this script was "Set" for setting an accessory's
60 | # specific attribute.
61 | if [ "$1" = "Set" ]; then
62 |
63 | # $2 would be the name of the accessory.
64 | # $3 would be the accessory's charactersistic 'On'.
65 | # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is.
66 | # $4 would be 'true' for 'On' and 'false' for 'Off' with
67 | # outputConstants=true in your .homebridge/.config.json file.
68 |
69 | # Handle the Set 'On' attribute of the accessory
70 | if [ "$3" = "On" ]; then
71 |
72 | # If the accessory is to be set on
73 | if [ "$4" = "1" ]; then
74 |
75 | # Normally we would exit immediately if a command fails with a non-zero status.
76 | # In this case ps4-waker can fail and we would rely on the failing exit status to
77 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
78 | # thing to do. However for this example we are going to output '0' (false) so
79 | # that you can see the '0' on the console telling us that the accessory is not
80 | # on the network.
81 | set +e
82 |
83 | # Execute the on command
84 | ps4-waker >> /dev/null 2>&1
85 |
86 | # keep the result of the on/off command
87 | rc=$?
88 |
89 | # Exit immediately if a command exits with a non-zero status
90 | set -e
91 |
92 | else
93 |
94 | # Normally we would exit immediately if a command fails with a non-zero status.
95 | # In this case ps4-waker can fail and we would rely on the failing exit status to
96 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
97 | # thing to do. However for this example we are going to output '0' (false) so
98 | # that you can see the '0' on the console telling us that the accessory is not
99 | # on the network.
100 | set +e
101 |
102 | # Execute the off command
103 | ps4-waker standby >> /dev/null 2>&1
104 |
105 | # keep the result of the on/off command
106 | rc=$?
107 |
108 | # Exit immediately if a command exits with a non-zero status
109 | set -e
110 | fi
111 |
112 | # Check if the on/off command had a positive return status.
113 | if [ "$rc" = "0" ]; then
114 |
115 | # The on/off command was successful, so exit successfully.
116 | exit 0
117 |
118 | else
119 | # The on/off comand had a failure result. Exit with that result.
120 |
121 | # Exit this script positivitely, even though ping failed.
122 | exit $rc
123 | fi
124 | fi
125 | fi
126 |
127 | # The proper arguments to this script were not passed to it so end with a failure exit status.
128 | exit 666
129 |
--------------------------------------------------------------------------------
/utils/Logger.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __importDefault = (this && this.__importDefault) || function ( mod ) {
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
4 | };
5 |
6 | const settings = require( "../cmd4Settings" );
7 |
8 | const util_1 = __importDefault( require( "util" ) );
9 | const chalk_1 = __importDefault( require( "chalk" ) );
10 | //
11 | // Log levels to indicate importance of the logged message.
12 | // Every level corresponds to a certain color.
13 | //
14 | // - INFO: no color
15 | // - WARN: yellow
16 | // - ERROR: red
17 | // - DEBUG: gray
18 | //
19 |
20 | //
21 | // Gets the prefix
22 | // @param prefix
23 | //
24 | function getLogPrefix( prefix )
25 | {
26 | return chalk_1.default.cyan( `[${prefix}]` );
27 | }
28 |
29 | //
30 | // Logger class
31 | //
32 | class Logger
33 | {
34 | constructor( )
35 | {
36 | // Note: log.info goes to logBuf
37 | this.logLineCount = 0;
38 | this.logBuf = "";
39 | // Note: log.warn goes to errBuf
40 | this.errLineCount = 0;
41 | this.errBuf = "";
42 |
43 | this.outputEnabled = true;
44 | this.bufferEnabled = false;
45 | this.debugEnabled = false;
46 |
47 | // Added as was set outside the class
48 | this.timestampEnabled = true;
49 | this.prefix = settings.PLATFORM_NAME;
50 | this.forceColor( );
51 | }
52 | //
53 | // Turns on debug level logging. Off by default.
54 | //
55 | // @param enabled { boolean }
56 | //
57 | setDebugEnabled( enabled = true )
58 | {
59 | this.debugEnabled = enabled;
60 | settings.cmd4Dbg = enabled;
61 | }
62 | setOutputEnabled( enabled = true )
63 | {
64 | this.outputEnabled = enabled;
65 | }
66 | //
67 | // Turns on buffered logging. Off by default.
68 | //
69 | // @param enabled { boolean }
70 | //
71 | setBufferEnabled( enabled = true )
72 | {
73 | this.bufferEnabled = enabled;
74 | }
75 | //
76 | // Turns on inclusion of timestamps in log messages. On by default.
77 | //
78 | // @param enabled { boolean }
79 | //
80 | setTimestampEnabled( enabled = true )
81 | {
82 | this.timestampEnabled = enabled;
83 | }
84 | //
85 | // Forces color in logging output, even if it seems like color is unsupported.
86 | //
87 | forceColor( )
88 | {
89 | chalk_1.default.level = 1; // `1` - Basic 16 colors support.
90 | }
91 | info( message, ...parameters )
92 | {
93 | this.log( "info" /* INFO */, message, ...parameters );
94 | }
95 | warn( message, ...parameters )
96 | {
97 | this.log( "warn" /* WARN */, message, ...parameters );
98 | }
99 | error( message, ...parameters )
100 | {
101 | this.log( "error" /* ERROR */, message, ...parameters );
102 | }
103 | debug( message, ...parameters )
104 | {
105 | if ( this.debugEnabled )
106 | this.log( "debug" /* DEBUG */, message, ...parameters );
107 | }
108 | log( level, message, ...parameters )
109 | {
110 | if ( level === "debug" /* DEBUG */ && ! this.debugEnabled )
111 | {
112 | return;
113 | }
114 | message = util_1.default.format( message, ...parameters );
115 | switch ( level )
116 | {
117 | case "warn":
118 | message = chalk_1.default.yellow( message );
119 | break;
120 | case "error":
121 | message = chalk_1.default.red( message );
122 | break;
123 | case "debug":
124 | message = chalk_1.default.gray( message );
125 | break;
126 | }
127 | if ( this.prefix )
128 | {
129 | message = getLogPrefix( this.prefix ) + " " + message;
130 | }
131 | if ( this.timestampEnabled )
132 | {
133 | const date = new Date();
134 | message = chalk_1.default.white( `[${date.toLocaleString()}] ` ) + message;
135 | }
136 | switch ( level )
137 | {
138 | // Homebridge puts warninggs to errorbuf, so do the same.
139 | case "error":
140 | case "warn":
141 | if ( this.bufferEnabled == true )
142 | {
143 | this.errLineCount++;
144 | this.errBuf += message;
145 | this.errBuf += "\n";
146 | }
147 | if ( this.outputEnabled == true )
148 | console.log( message );
149 | break;
150 | case "debug":
151 | case "info":
152 | default:
153 | if ( this.bufferEnabled == true )
154 | {
155 | this.logLineCount++;
156 | this.logBuf += message;
157 | this.logBuf += "\n";
158 | }
159 | if ( this.outputEnabled == true )
160 | console.log( message );
161 | }
162 |
163 | }
164 | reset()
165 | {
166 | this.logLineCount = 0;
167 | this.logBuf = "";
168 | this.errLineCount = 0;
169 | this.errBuf = "";
170 | }
171 | }
172 | module.exports=Logger;
173 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/wakeonlan.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #
4 | # This Cmd4 example demonstrates a script that can be used for a wakeonlan
5 | # scenario. It is a port of the cmdSwitch2 example and is more for a Windows
6 | # PC.
7 | #
8 | # Your Cmd4 .homebridge/.config.json file would have a state_cmd like:
9 | # state_cmd: ".homebridge/Cmd4Scripts/Examples/wakeonlan.sh"
10 | # state_cmd_suffix: "192.168.2.66 dc:a6:32:40:de:7c"
11 | #
12 | #
13 | # Testing from the shell prompt:
14 | # ./wakeonlan.sh Get HTPC On 192.168.2.66 dc:a6:32:40:de:7c
15 | # or
16 | # ./wakeonlan.sh Set HTPC On 1 192.168.2.66 dc:a6:32:40:de:7c
17 | # or
18 | # ./wakeonlan.sh Set HTPC On 0 192.168.2.66 dc:a6:32:40:de:7c
19 |
20 | # Exit immediately if a command exits with a non-zero status
21 | set -e
22 |
23 | # Check if the first parameter to this script was "Get" for getting an accessory's
24 | # specific attribute.
25 | if [ "$1" = "Get" ]; then
26 |
27 | # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fourth
28 | # parameter to a Get command.
29 | ip="${4}"
30 |
31 | # Cmd4 will pass the MAC Address in the config.json defined by state_cmd_suffix as the fifth
32 | # parameter to a Get command.
33 | macAddress="${5}"
34 |
35 | # Normally we would exit immediately if a command fails with a non-zero status.
36 | # In this case ping can fail and we would rely on the failing exit status to
37 | # tell Cmd4 that the accessory is not on the network. That would be the prefered
38 | # thing to do. However for this example we are going to output '0' (false) so
39 | # that you can see the '0' on the console telling us that the accessory is not
40 | # on the network.
41 | set +e
42 |
43 | # On OSX the string is returned differently than on linux.
44 | ping -c 2 -W 1 "${ip}" | sed -E 's/2 packets received/2 received/g' | grep -i '2 received' >> /dev/null
45 | rc=$?
46 |
47 | # Exit immediately if a command exits with a non-zero status
48 | set -e
49 |
50 | # Check if we got the message '2 packets recieved' meaning the accessory is
51 | # on the network by seeing if the return code of the above command passed or
52 | # failed.
53 | if [ "$rc" = "0" ]; then
54 | # The message was recieved so the target is up, sending a '1' (true), like
55 | # a binary number is, back to Cmd4.
56 | echo "1"
57 |
58 | # Exit this script positivitely.
59 | exit 0
60 | else
61 | # The message was not recieved so the target must be down, sending a '0' (false), like
62 | # a binary number is, back to Cmd4.
63 | echo "0"
64 |
65 | # Exit this script positivitely, even though ping failed.
66 | exit 0
67 | fi
68 | fi
69 |
70 | # Check if the first parameter to this script was "Set" for setting an accessory's
71 | # specific attribute.
72 | if [ "$1" = "Set" ]; then
73 |
74 | # $2 would be the name of the accessory.
75 | # $3 would be the accessory's charactersistic 'On'.
76 | # $4 would be '1' for 'On' and '0' for 'Off', like a binary number is.
77 | # $4 would be 'true' for 'On' and 'false' for 'Off' with
78 | # outputConstants=true in your .homebridge/.config.json file.
79 |
80 | # Cmd4 will pass the IP in the config.json defined by state_cmd_suffix as the fifth
81 | # parameter to a Set command.
82 | ip="${5}"
83 |
84 | # Cmd4 will pass the MAC Address in the config.json defined by state_cmd_suffix as the sixth
85 | # parameter to a Set command.
86 | macAddress="${6}"
87 |
88 | # Handle the Set 'On' attribute of the accessory
89 | if [ "$3" = "On" ]; then
90 |
91 | # If the accessory is to be set on
92 | if [ "$4" = "1" ]; then
93 | # Execute the on command
94 | wakeonlan -i ${ip} ${macAddress} >> /dev/null 2>&1
95 |
96 | # keep the result of the on/off command
97 | rc=$?
98 | else
99 | # Execute the off command
100 | # The password is harad coded here. We use the default
101 | # Raspberry Pi password asli an example. How you handle
102 | # this unencrypted password is up to you. Note that
103 | # this command only works to a Windows box, but is the
104 | # exmple given in cmdSwitch2.
105 | # net rpc shutdown -I "${ip}" -U user%password,
106 | net rpc shutdown -I "${ip}" -U pi%raspberry >> /dev/null 2>&1
107 |
108 | # keep the result of the on/off command
109 | rc=$?
110 | fi
111 |
112 | # Check if the on/off command had a positive return status.
113 | if [ "$rc" = "0" ]; then
114 |
115 | # The on/off command was successful, so exit successfully.
116 | exit 0
117 |
118 | else
119 | # The on/off comand had a failure result. Exit with that result.
120 |
121 | # Exit this script positivitely, even though ping failed.
122 | exit $rc
123 | fi
124 | fi
125 | fi
126 |
127 | # The proper arguments to this script were not passed to it so end with a failure exit status.
128 | exit 66
129 |
--------------------------------------------------------------------------------
/Extras/Cmd4Scripts/Examples/ExampleJavaScript_template.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | // ExampleScript_template.js
3 | //
4 | // Description:
5 | // This script is a goood starting place for you to create Cmd4 Scripts
6 | // of your own
7 | //
8 | // Parameters are:
9 | // Get < Any accessory name > < Characteristic >
10 | // Set < Any accessory name > < Characteristic > < value >
11 | //
12 | // Note 1: These paramaters match the those of the Cmd4 plugin.
13 | // A full lost of supported devices and characteristics can be
14 | // found at:
15 | // https://ztalbot2000.github.io/homebridge-cmd4
16 | //
17 | // How it works:
18 | // The Cmd4 plugin will call this script to retrieve those states
19 | // you have defined as not Cached to Get/Set your devices characteristic
20 | // states.
21 | //
22 | // For example:
23 | // node ExampleScript_template.js Set My_Door TargetDoorState 0
24 | // or
25 | // node ExampleScript.js Get My_Door CurrentDoorState
26 | //
27 | //
28 |
29 | 'use strict';
30 |
31 | var length = process.argv.length;
32 | var device = "My_Door";
33 | var io = "";
34 | var characteristic = "";
35 | var option = "";
36 |
37 | if ( length == 2 ) process.exit( 0 );
38 |
39 | if ( length <= 2 ) {
40 | console.log( "Usage: " + process.argv[0] + " < Get > < AccessoryName > < Characteristic >" );
41 | console.log( " " + process.argv[0] + " < Set > < AccessoryName > < Characteristic > < Value >" );
42 | process.exit( -1 );
43 | }
44 |
45 | if ( length >= 2 ) io = process.argv[2];
46 | if ( length >= 3 ) device = process.argv[3];
47 | if ( length >= 4 ) characteristic = process.argv[4];
48 | if ( length >= 5 ) option = process.argv[5];
49 |
50 | var c = "";
51 |
52 | switch( io )
53 | {
54 | case "Get":
55 | {
56 | switch( characteristic )
57 | {
58 | case "CurrentDoorState":
59 | {
60 | console.log( 0 );
61 |
62 | // See https://ztalbot2000.github.io/homebridge-cmd4
63 | // For the possible values and characteristics
64 | // available per device. It will show somethink like:
65 | // Valid Values:
66 | // 0 - "Open. The door is fully open."
67 | // 1 - "Closed. The door is fully closed."
68 | // 2 - "Opening. The door is actively opening."
69 | // 3 - "Closing. The door is actively closing."
70 | // 4 - "Stopped. The door is not moving, and it is not fully
71 | // open nor fully closed."
72 | // 5-255 - "Reserved"
73 |
74 | break;
75 | }
76 | case "TargetDoorState":
77 | {
78 | console.log( 0 );
79 | break;
80 | }
81 | case "ObstructionDetected":
82 | {
83 | console.log ( 0 );
84 | break;
85 | }
86 | case "LockCurrentState":
87 | {
88 | console.log ( 0 );
89 | break;
90 | }
91 | default:
92 | console.error( "Unhandled characteristic for:" + io + " Device:" + device + " Characteristic:" + characteristic );
93 | process.exit( -1 );
94 | }
95 |
96 | break;
97 |
98 | } // End of Switch for "Get"
99 | case "Set":
100 | {
101 | switch( characteristic )
102 | {
103 | case "CurrentDoorState":
104 | {
105 | // Current Door State is not settable. The
106 | // call would be to TargetDoorState. This is here
107 | // for debugging only.
108 |
109 | break;
110 | }
111 | case "TargetDoorState":
112 | {
113 | // Do something of your own here.
114 |
115 | break;
116 | }
117 | case "ObstructionDetected":
118 | {
119 | // Obstruction Detected is not settable. It
120 | // call is a read-only characteristic. This is here
121 | // for debugging only.
122 | break;
123 | }
124 | case "LockCurrentState":
125 | {
126 | // Lock Current State is not settable. It
127 | // call is a read-only characteristic. This is here
128 | // for debugging only.
129 | break;
130 | }
131 | default:
132 | console.error( "UnHandled Characteristic for:" + io + " Device:" + device + " Characteristic:" + characteristic );
133 | process.exit( -1 );
134 | }
135 |
136 | break;
137 | } // End of Switch Device for "Set"
138 | default:
139 | console.error( "Unknown IO" + io );
140 | process.exit( -1 );
141 | }
142 |
143 | //console.log( "Say What Device:" + device + " Characteristic:" + characteristic + " Option:" + option );
144 |
145 | // You must exit with a zero status, confirming the script rannsuccessfully.
146 | process.exit( 0 );
147 |
148 |
149 |
--------------------------------------------------------------------------------
/utils/Cmd4Storage.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // These would already be initialized by index.js
4 | let CMD4_ACC_TYPE_ENUM = require( "../lib/CMD4_ACC_TYPE_ENUM" ).CMD4_ACC_TYPE_ENUM;
5 |
6 | class Cmd4Storage
7 | {
8 | constructor( log, cmd4Storage )
9 | {
10 | this.CLASS_VERSION = 1;
11 | this.DATA = [ ];
12 |
13 | if ( cmd4Storage == undefined )
14 | {
15 | log.debug("Init new cmd4Storage" );
16 |
17 | // init new
18 | this.DATA = new Array( CMD4_ACC_TYPE_ENUM.EOL ).fill( null );
19 |
20 | } else if ( cmd4Storage instanceof Cmd4Storage )
21 | {
22 | log.debug("Class is Cmd4Storage version: %s", cmd4Storage.version );
23 | if ( cmd4Storage.CLASS_VERSION == this.CLASS_VERSION )
24 | {
25 | // The same. I can just load its data
26 | this.loadLatestData( cmd4Storage.DATA );
27 | } else {
28 | throw new Error( `Do not know how to handle Cmd4_Storage Class version: ${ cmd4Storage.CLASS_VERSION }` );
29 | }
30 | } else if ( Array.isArray( cmd4Storage ) )
31 | {
32 | log.debug("Cmd4Storage is Array" );
33 | // Init original unversioned
34 | let data = this.upgradeDataArray( 0, cmd4Storage );
35 | this.loadLatestData( data );
36 |
37 | } else if ( cmd4Storage.constructor === Object )
38 | {
39 | log.debug("Cmd4Storage is Object version %s", cmd4Storage.CLASS_VERSION );
40 | if ( cmd4Storage.CLASS_VERSION == this.CLASS_VERSION )
41 | {
42 | // The same. I can just load its data
43 | this.loadLatestData( cmd4Storage.DATA );
44 | } else {
45 | throw new Error( `Do not know how to handle Cmd4_Storage Class version: ${ cmd4Storage.CLASS_VERSION }` );
46 | }
47 | } else
48 | {
49 | // Woops init new
50 | log.error( "cmd4Storage is %s", cmd4Storage );
51 | console.error( "cmd4Storage.constructor.name is %s", cmd4Storage.constructor.name );
52 | throw new Error( `Do not know how to handle typeof: ${ typeof cmd4Storage } Cmd4_Storage parm: ${ cmd4Storage }` );
53 | }
54 | }
55 |
56 | upgradeDataArray( fromVersion, fromData)
57 | {
58 | let data = [ ];
59 |
60 | if ( fromVersion != 0 )
61 | throw new Error( `Do not know how to handle Cmd4_Storage version: ${ fromVersion }` );
62 |
63 | // Version 0 ACC_DATA went from 0-122
64 | // This version goes from 1-123 and changes to
65 | // Assoc array so that index changes like this will no longer
66 | // impact the storage schema as much
67 | let i=0;
68 | for ( i=0; i < CMD4_ACC_TYPE_ENUM.ListPairings; i++ )
69 | {
70 | data[ i ] = fromData[ i ];
71 | }
72 | data[ CMD4_ACC_TYPE_ENUM.ListPairing ] = null;
73 | for ( i = CMD4_ACC_TYPE_ENUM.ListPairings +1; i < CMD4_ACC_TYPE_ENUM.EOL; i++ )
74 | {
75 | data[ i ] = fromData[ i - 1 ];
76 | }
77 | return data;
78 | }
79 |
80 | loadLatestData( data )
81 | {
82 | this.DATA = data;
83 | }
84 |
85 | getStoredValueForIndex( accTypeEnumIndex )
86 | {
87 | if ( accTypeEnumIndex < 0 || accTypeEnumIndex >= CMD4_ACC_TYPE_ENUM.EOL )
88 | throw new Error( `getStoredValue - Characteristic index: ${ accTypeEnumIndex } not between 0 and ${ CMD4_ACC_TYPE_ENUM.EOL }\nCheck your config.json file for unknown characteristic.` );
89 |
90 |
91 | return this.DATA[ accTypeEnumIndex ];
92 | }
93 |
94 | getStoredValueForCharacteristic( characteristicString )
95 | {
96 | let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.indexOfEnum( characteristicString );
97 |
98 | return this.getStoredValueForIndex( accTypeEnumIndex );
99 | }
100 | setStoredValueForIndex( accTypeEnumIndex, value )
101 | {
102 | if ( accTypeEnumIndex < 0 || accTypeEnumIndex >= CMD4_ACC_TYPE_ENUM.EOL )
103 | throw new Error( `setStoredValue - Characteristic index: ${ accTypeEnumIndex } not between 0 and ${ CMD4_ACC_TYPE_ENUM.EOL }\nCheck your config.json file for unknown characteristic.` );
104 |
105 | this.DATA[ accTypeEnumIndex ] = value;
106 | }
107 | setStoredValueForCharacteristic( characteristicString, value )
108 | {
109 | let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.indexOfEnum( characteristicString );
110 |
111 | this.setStoredValueForIndex( accTypeEnumIndex, value );
112 | }
113 |
114 | // Unlike get/set, testStoredValueForIndex does not call process.exit,
115 | // but undefined for an illegal range, in the case that rogue runtime data
116 | // dies not take down CMD4.
117 | testStoredValueForIndex( accTypeEnumIndex )
118 | {
119 | if ( accTypeEnumIndex < 0 || accTypeEnumIndex > CMD4_ACC_TYPE_ENUM.EOL )
120 | return undefined;
121 |
122 | return this.DATA[ accTypeEnumIndex ];
123 | }
124 | testStoredValueForCharacteristic( characteristicString )
125 | {
126 | let accTypeEnumIndex = CMD4_ACC_TYPE_ENUM.indexOfEnum( characteristicString );
127 |
128 | return this.testStoredValueForIndex( accTypeEnumIndex );
129 | }
130 | }
131 |
132 | module.exports = Cmd4Storage;
133 |
--------------------------------------------------------------------------------
/test/CMD4_CHAR_TYPE_ENUMS.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _api = new HomebridgeAPI(); // object we feed to Plugins
4 |
5 | let { indexOfEnum } = require( "../utils/indexOfEnum" );
6 | Object.defineProperty(exports, "indexOfEnum", { enumerable: true, get: function () { return indexOfEnum.indexOfEnum; } });
7 |
8 |
9 | describe( "Testing require of CMD4_CHAR_TYPE_ENUMS.js", ( ) =>
10 | {
11 | it( "CMD4_CHAR_TYPE_ENUMS should be defined ( required correctly )", ( ) =>
12 | {
13 | assert.isNotNull( CMD4_CHAR_TYPE_ENUMS, "CMD4_CHAR_TYPE_ENUMS is null" );
14 | });
15 |
16 | // ************ TEST UNINITIALIZED CMD4_FORMAT_TYPE_ENUM EOL **************
17 | describe( "Testing CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL", ( ) =>
18 | {
19 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM has EOL", ( ) =>
20 | {
21 | assert.isNotNull( CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL, "EOL is null" );
22 | });
23 |
24 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL = " + FORMAT_EOL, ( ) =>
25 | {
26 | 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 );
27 | });
28 |
29 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM[ 0-" + CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL + " ] should equal value at index", ( ) =>
30 | {
31 | for ( let index=0; index < CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.EOL; index ++ )
32 | {
33 | assert.notEqual( CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM[index], index );
34 | }
35 | });
36 |
37 | });
38 |
39 | // ************ TEST UNINITIALIZED CMD4_UNITS_TYPE_ENUM EOL **************
40 | describe( "Testing CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL", ( ) =>
41 | {
42 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM has EOL", ( ) =>
43 | {
44 | assert.isNotNull( CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL, "EOL is null" );
45 | });
46 |
47 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL = " + UNITS_EOL, ( ) =>
48 | {
49 | 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 );
50 | });
51 |
52 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM[ 0-" + CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL + " ] should equal value at index", ( ) =>
53 | {
54 | for ( let index=0; index < CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.EOL; index ++ )
55 | {
56 | assert.notEqual( CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM[index], index );
57 | }
58 | });
59 |
60 | });
61 |
62 |
63 | // ************ TEST UNINITIALIZED CMD4_PERMS_TYPE_ENUM EOL **************
64 | describe( "Testing CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL", ( ) =>
65 | {
66 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM has EOL", ( ) =>
67 | {
68 | assert.isNotNull( CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL, "EOL is null" );
69 | });
70 |
71 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL = " + PERMS_EOL, ( ) =>
72 | {
73 | 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 );
74 | });
75 |
76 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM[ 0-" + CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL + " ] should equal value at index", ( ) =>
77 | {
78 | for ( let index=0; index < CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.EOL; index ++ )
79 | {
80 | assert.notEqual( CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM[index], index );
81 | }
82 | });
83 |
84 | });
85 | })
86 |
87 |
88 |
89 |
90 |
91 | describe( "Testing INITIALIZED CMD4_CHAR_TYPE_ENUMS", ( ) =>
92 | {
93 | // Init the library for all to use
94 | let CMD4_CHAR_TYPE_ENUMS = CHAR_DATA.init( _api.hap.Formats, _api.hap.Units, _api.hap.Perms );
95 |
96 |
97 | describe("Testing Initialized CMD4_CHAR_TYPE_ENUMS.", ( ) =>
98 | {
99 | // Test a format type
100 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.float should equal expected value", ( ) =>
101 | {
102 | let formatIndex = CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM.properties.indexOfEnum( i => i.type === "float" );
103 | assert.equal( formatIndex, 2, `CMD4_CHAR_TYPE_ENUMS.CMD4_FORMAT_TYPE_ENUM "float" not equal to expected index`);
104 | });
105 |
106 | // Test a units type
107 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.lux should equal expected value", ( ) =>
108 | {
109 | let unitsIndex = CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM.properties.indexOfEnum( i => i.type === "lux" );
110 | assert.equal( unitsIndex, 3, `CMD4_CHAR_TYPE_ENUMS.CMD4_UNITS_TYPE_ENUM "lux" not equal to expected index`);
111 | });
112 |
113 | // Test a perms type
114 | it( "CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.ev should equal expected value", ( ) =>
115 | {
116 | let permsIndex = CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM.properties.indexOfEnum( i => i.type === "ev" );
117 | assert.equal( permsIndex, 4, `CMD4_CHAR_TYPE_ENUMS.CMD4_PERMS_TYPE_ENUM "ev" not equal to expected index`);
118 | });
119 | });
120 | });
121 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "homebridge-cmd4",
3 | "description": "Exec Plugin for Homebridge supporting all accessorys and characteristics",
4 | "version": "8.0.3",
5 | "license": "MIT",
6 | "author": {
7 | "name": "John Talbot"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git://github.com/ztalbot2000/homebridge-cmd4.git"
12 | },
13 | "homepage": "https://github.com/ztalbot2000/homebridge-cmd4#readme",
14 | "changelog": "https://github.com/ztalbot2000/homebridge-cmd4/blob/master/ChangeLog.md",
15 | "bugs": {
16 | "url": "https://github.com/ztalbot2000/homebridge-cmd4/issues",
17 | "email": "ztalbot2000@gmail.com"
18 | },
19 | "dependencies": {
20 | "chalk": "^4.1.0",
21 | "command-exists": "^1.2.9",
22 | "fakegato-history": ">=0.6.5",
23 | "latest-version": "^5.1.0",
24 | "moment": "*"
25 | },
26 | "devDependencies": {
27 | "@commitlint/cli": "^11.0.0",
28 | "@commitlint/config-conventional": "^11.0.0",
29 | "chai": "^4.2.0",
30 | "commander": "^8.2.0",
31 | "commitlint-plugin-function-rules": "^1.1.20",
32 | "eslint": "^7.17.0",
33 | "generate-changelog": "^1.8.0",
34 | "husky": "^4.3.8",
35 | "link-checker": "^1.4.2",
36 | "markdown-link-check": "^3.8.6",
37 | "mocha": "^8.0.1",
38 | "node-persist": "^0.0.11",
39 | "sinon": "^9.2.4",
40 | "which": "^2.0.2"
41 | },
42 | "directories": {
43 | "test": "test",
44 | "lib": "lib",
45 | "utils": "utils",
46 | "docs": "docs"
47 | },
48 | "engines": {
49 | "node": "^18.20.4 || ^20.18.0 || ^22.10.0",
50 | "homebridge": "^1.8.0 || ^2.0.0-beta.0"
51 | },
52 | "keywords": [
53 | "homebridge-plugin",
54 | "homebridge",
55 | "exec",
56 | "command",
57 | "switch",
58 | "light",
59 | "door",
60 | "thermostat",
61 | "security",
62 | "temperature",
63 | "virtual",
64 | "Eve",
65 | "Homekit",
66 | "siri",
67 | "home",
68 | "Eve"
69 | ],
70 | "husky": {
71 | "hooks": {
72 | "pre-commit": "npm run lint && npm run test test/cmd4Constants",
73 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
74 | }
75 | },
76 | "scripts": {
77 | "debug": "DEBUG=* homebridge --debug --plugin-path .",
78 | "pretest": "printf ' test is only done in a development environment\n';sleep 2 ",
79 | "testDebug": "node_modules/.bin/mocha --require async-dump.js --file test/mocha-setup --ignore test/versionChecker.js",
80 | "test": "node_modules/.bin/mocha --file test/mocha-setup --ignore test/versionChecker.js",
81 | "singleTest": "node_modules/.bin/mocha --bail --file test/mocha-setup --ignore test/versionChecker.js",
82 | "allTests": "node_modules/.bin/mocha --bail --file test/mocha-setup $( cat test/allTests )",
83 | "sanityTests": "node_modules/.bin/mocha --bail --file test/mocha-setup $( cat test/sanityTests )",
84 | "_Note0": "Excluded from commit as may fail for probing GitHub to much",
85 | "testMarkDown": "markdown-link-check ./README.md && markdown-link-check ./CHANGELOG.md && markdown-link-check ./docs/AdvancedTroubleShooting.md && markdown-link-check ./docs/Developers.md",
86 | "testAutoGeneratedDocLinks": "link-checker docs/autoGenerated/CMD4_AccessoryDescriptions.html",
87 | "commitTests": "npm run test && npm run testMarkDown",
88 | "_Note": "Excluded only because they sometimes fail, but pass alone. ",
89 | "testCmd4Accessory": "node_modules/.bin/mocha --file test/mocha-setup test/Cmd4Accessory.js",
90 | "testVersionChecker": "node_modules/.bin/mocha --file test/mocha-setup test/versionChecker.js",
91 | "lint": "eslint --ext .js *.js utils/*.js lib/*.js tools/* test/*.js",
92 | "postinstall": "node postinstall.js",
93 | "_Note01": "Auto change the version based on the commit history. ",
94 | "_Note02": "Where: ",
95 | "_Note03": "To trigger a version update ",
96 | "_Note04": " npm run release ",
97 | "_Note05": " ",
98 | "_Note06": "To trigger a patch update (1.0.0 → 1.0.1) ",
99 | "_Note07": " git commit -m 'fix: …' ",
100 | "_Note08": "To trigger a minor update (1.0.0 → 1.1.0) ",
101 | "_Note09": " git commit -m 'feat: …' ",
102 | "_Note10": "To trigger a major update (1.0.0 → 2.0.0) ",
103 | "_Note11": " A BREAKING CHANGE: in the commit body ",
104 | "_Note12": " ",
105 | "_Note13": "As a summary, generate-changelog will do ",
106 | "_Note14": " * Bumps the version in package.json ",
107 | "_Note15": " * Updates CHANGELOG.md ",
108 | "_Note16": " ",
109 | "release": "tools/generateChangeLog",
110 | "_Note17": " ",
111 | "_Note18": "Overide default behaviour with: ",
112 | "release:patch": "tools/generateChangeLog --type patch --cleanup",
113 | "release:minor": "tools/generateChangeLog --type minor --cleanup",
114 | "release:major": "tools/generateChangeLog --type major --cleanup"
115 | }
116 | }
--------------------------------------------------------------------------------
/test/HV.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 | let HV = require( "../utils/HV" );
5 | let constants = require( "../cmd4Constants" );
6 |
7 |
8 | describe(`A Hierarchy Variable Test`, ( ) =>
9 | {
10 | it( `Test creation of HV`, ( ) =>
11 | {
12 | const hv = new HV( );
13 |
14 | assert.instanceOf( hv , HV, "Expected hv to be instance of HV. Found %s" , hv );
15 | assert.isFunction( hv.update, ".update is not a function" );
16 |
17 | assert.equal( hv.allowTLV8, constants.DEFAULT_ALLOW_TLV8, `default not created` );
18 | assert.equal( hv.debug, constants.DEFAULT_DEBUG, `default not created` );
19 | assert.equal( hv.outputConstants, constants.DEFAULT_OUTPUTCONSTANTS, `default not created` );
20 | assert.equal( hv.interval, constants.DEFAULT_INTERVAL, `default not created` );
21 | assert.equal( hv.stateChangeResponseTime, constants.DEFAULT_STATE_CHANGE_RESPONSE_TIME, `default not created` );
22 | assert.equal( hv.statusMsg, constants.DEFAULT_STATUSMSG, `default not created` );
23 | assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `default not created` );
24 |
25 |
26 | });
27 |
28 | it( `HV can update variables`, ( done ) =>
29 | {
30 | let data =
31 | {
32 | allowTLV8: false,
33 | debug: true,
34 | outputConstants: true,
35 | interval: 100,
36 | stateChangeResponseTime: 122,
37 | statusMsg: false,
38 | timeout: 141
39 | };
40 | let hv = new HV( );
41 |
42 | // Simple check previously tested
43 | assert.equal( hv.allowTLV8, constants.DEFAULT_ALLOW_TLV8, `default not created` );
44 |
45 | hv.update( data );
46 |
47 | assert.equal( hv.allowTLV8, data.allowTLV8, `hv not updated` );
48 | assert.equal( hv.debug, data.debug, `hv not updated` );
49 | assert.equal( hv.outputConstants, data.outputConstants, `hv not updated` );
50 | assert.equal( hv.interval, data.interval, `hv not updated` );
51 | assert.equal( hv.stateChangeResponseTime, data.stateChangeResponseTime, `hv not updated` );
52 | assert.equal( hv.statusMsg, data.statusMsg, `hv not updated` );
53 | assert.equal( hv.timeout, data.timeout, `hv not updated` );
54 |
55 | done( );
56 | });
57 |
58 | it( `Test HV can update updated variables`, ( done ) =>
59 | {
60 | let data =
61 | {
62 | allowTLV8: false,
63 | debug: true,
64 | outputConstants: true,
65 | interval: 100,
66 | stateChangeResponseTime: 122,
67 | statusMsg: false,
68 | timeout: 141
69 | };
70 | let hv = new HV( );
71 |
72 | hv.update( data );
73 |
74 | let data2 =
75 | {
76 | allowTLV8: true,
77 | debug: false,
78 | outputConstants: false,
79 | interval: 300,
80 | // stateChangeResponseTime: 122,
81 | statusMsg: true,
82 | timeout: 333
83 | };
84 |
85 | hv.update( data2 );
86 |
87 | assert.equal( hv.allowTLV8, data2.allowTLV8, `hv not updated` );
88 | assert.equal( hv.debug, data2.debug, `hv not updated` );
89 | assert.equal( hv.outputConstants, data2.outputConstants, `hv not updated` );
90 | assert.equal( hv.interval, data2.interval, `hv not updated` );
91 | assert.equal( hv.stateChangeResponseTime, data.stateChangeResponseTime, `hv not updated` );
92 | assert.equal( hv.statusMsg, data2.statusMsg, `hv not updated` );
93 | assert.equal( hv.timeout, data2.timeout, `hv not updated` );
94 |
95 | done( );
96 | });
97 |
98 | it( `Test HV copy does not change original`, ( done ) =>
99 | {
100 | const hv = new HV( );
101 |
102 | let rHV = Object.assign( {}, hv );
103 | assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `hv not initialized` );
104 |
105 | rHV.timeout = 1500;
106 |
107 | assert.equal( rHV.timeout, 1500, `rhv not changed` );
108 | assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `hv was changed` );
109 |
110 | done( );
111 | });
112 |
113 | it( `Test HV copy does not change original`, ( done ) =>
114 | {
115 | const hv = new HV( );
116 |
117 | let rHV = Object.assign( {}, hv );
118 | rHV.update = hv.update;
119 |
120 | assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `hv not initialized` );
121 |
122 | let data =
123 | {
124 | allowTLV8: true,
125 | debug: false,
126 | outputConstants: false,
127 | interval: 300,
128 | // stateChangeResponseTime: 122,
129 | statusMsg: true,
130 | timeout: 333
131 | };
132 |
133 | rHV.update( data );
134 |
135 | // Check changed
136 | assert.equal( rHV.allowTLV8, data.allowTLV8, `rHV not updated` );
137 | assert.equal( rHV.debug, data.debug, `rHV not updated` );
138 | assert.equal( rHV.outputConstants, data.outputConstants, `rHV not updated` );
139 | assert.equal( rHV.interval, data.interval, `rHV not updated` );
140 | assert.equal( rHV.stateChangeResponseTime, constants.DEFAULT_STATE_CHANGE_RESPONSE_TIME, `default not created` );
141 | assert.equal( rHV.statusMsg, data.statusMsg, `rHV not updated` );
142 | assert.equal( rHV.timeout, data.timeout, `rHV not updated` );
143 |
144 | // Check original
145 | assert.equal( hv.allowTLV8, constants.DEFAULT_ALLOW_TLV8, `default was changed` );
146 | assert.equal( hv.debug, constants.DEFAULT_DEBUG, `default was changed` );
147 | assert.equal( hv.outputConstants, constants.DEFAULT_OUTPUTCONSTANTS, `default was changed` );
148 | assert.equal( hv.interval, constants.DEFAULT_INTERVAL, `default was changed` );
149 | assert.equal( hv.stateChangeResponseTime, constants.DEFAULT_STATE_CHANGE_RESPONSE_TIME, `default was changed` );
150 | assert.equal( hv.statusMsg, constants.DEFAULT_STATUSMSG, `default was changed` );
151 | assert.equal( hv.timeout, constants.DEFAULT_TIMEOUT, `default was changed` );
152 |
153 |
154 | done( );
155 |
156 | });
157 | });
158 |
159 |
--------------------------------------------------------------------------------
/test/getAccessoryNameFunctions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { getAccessoryName, getAccessoryDisplayName } = require( "../utils/getAccessoryNameFunctions" );
4 |
5 | describe( "Testing getAccessoryName LC", ( ) =>
6 | {
7 | it( "getAccessoryName.js should be a function", ( ) =>
8 | {
9 |
10 | assert.isFunction( getAccessoryName, "getAccessoryName is not a function" );
11 | });
12 |
13 | it( "getAccessoryName should choose name first LC ", ( ) =>
14 | {
15 | let config = {displayName: "Kodi",
16 | name: "blah"
17 | };
18 | let expectedResult = "blah";
19 | let result = getAccessoryName( config );
20 |
21 | assert.equal( result, expectedResult, "getAccessoryName should return name over displayName. result: " + result + " expected: " + expectedResult );
22 | });
23 |
24 | it( "getAccessoryName should choose displayName second LC ", ( ) =>
25 | {
26 | let config = { noName: "blah",
27 | displayName: "Kodi"
28 | };
29 | let expectedResult = "Kodi";
30 | let result = getAccessoryName( config );
31 |
32 | assert.equal( result, expectedResult, "getAccessoryName should return displayName over name. result: " + result + " expected: " + expectedResult );
33 | });
34 | })
35 |
36 | describe( "Testing getAccessoryDisplayName LC", ( ) =>
37 | {
38 | it( "getAccessoryDisplayName.js should be a function LC", ( ) =>
39 | {
40 |
41 | assert.isFunction( getAccessoryDisplayName, "getAccessoryDisplayName is not a function" );
42 | });
43 |
44 | it( "getAccessoryDisplayName should choose displayName first LC ", ( ) =>
45 | {
46 | let config = {displayName: "Kodi",
47 | name: "blah"
48 | };
49 | let expectedResult = "Kodi";
50 | let result = getAccessoryDisplayName( config );
51 |
52 | assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult );
53 | });
54 |
55 | it( "getAccessoryDisplayName should choose name second LC ", ( ) =>
56 | {
57 | let config = { name: "Kodi",
58 | configuredName: "blah"
59 | };
60 | let expectedResult = "Kodi";
61 | let result = getAccessoryDisplayName( config );
62 |
63 | assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult );
64 | });
65 | })
66 |
67 |
68 | describe( "Testing getAccessoryName UC", ( ) =>
69 | {
70 | it( "getAccessoryName should choose name first UC ", ( ) =>
71 | {
72 | let config = {displayName: "Kodi",
73 | name: "blah"
74 | };
75 | let expectedResult = "blah";
76 | let result = getAccessoryName( config );
77 |
78 | assert.equal( result, expectedResult, "getAccessoryName should return name over displayName. result: " + result + " expected: " + expectedResult );
79 | });
80 |
81 | it( "getAccessoryName should choose displayName second UC ", ( ) =>
82 | {
83 | let config = { NoName: "blah",
84 | displayName: "Kodi"
85 | };
86 | let expectedResult = "Kodi";
87 | let result = getAccessoryName( config );
88 |
89 | assert.equal( result, expectedResult, "getAccessoryName should return displayName over name. result: " + result + " expected: " + expectedResult );
90 | });
91 | })
92 |
93 | describe( "Testing getAccessoryDisplayName.js", ( ) =>
94 | {
95 | it( "getAccessoryDisplayName.js should be a function UC", ( ) =>
96 | {
97 |
98 | assert.isFunction( getAccessoryDisplayName, "getAccessoryDisplayName is not a function" );
99 | });
100 |
101 | it( "getAccessoryDisplayName should choose displayName first UC ", ( ) =>
102 | {
103 | let config = {displayName: "Kodi",
104 | name: "blah"
105 | };
106 | let expectedResult = "Kodi";
107 | let result = getAccessoryDisplayName( config );
108 |
109 | assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult );
110 | });
111 |
112 | it( "getAccessoryDisplayName should choose name second UC ", ( ) =>
113 | {
114 | let config = { name: "Kodi",
115 | configuredName: "blah"
116 | };
117 | let expectedResult = "Kodi";
118 | let result = getAccessoryDisplayName( config );
119 |
120 | assert.equal( result, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + result + " expected: " + expectedResult );
121 | });
122 | })
123 |
124 | /* Mucking about. Result, just use getAccessoryName
125 | describe( "Testing logic of config.name", ( ) =>
126 | {
127 | it.skip( "name should equal whatever config.[Nn]ame is", ( ) =>
128 | {
129 | let config = { Name: "Np",
130 | ConfiguredName: "Np"
131 | };
132 |
133 | let configPassedToGetAccessoryNane = { Name: "Kodi",
134 | ConfiguredName: "blah"
135 | };
136 |
137 | // This does not do what I thought
138 | let name = config.name = config.name = config.Name || getAccessoryName( configPassedToGetAccessoryNane );
139 |
140 | let expectedResult = "Kodi";
141 |
142 | // This fails with:
143 | // AssertionError: getAccessoryDisplayName should return displayName over name. result: Np expected: Kodi: expected 'Np' to equal 'Kodi'
144 | // + expected - actual
145 | // -Np
146 | // +Kodi
147 | assert.equal( name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult );
148 | assert.equal( config.name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult );
149 | assert.equal( config.Name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult );
150 |
151 | });
152 |
153 | it.skip( "name should equal whatever config.[Nn]ame is", ( ) =>
154 | {
155 | let config = { Name: "Np",
156 | ConfiguredName: "Np"
157 | };
158 |
159 | let configPassedToGetAccessoryNane = { Name: "Kodi",
160 | ConfiguredName: "blah"
161 | };
162 |
163 | // This does do what is expected, but config.Name never gets set.
164 | let name = config.name = config.name || getAccessoryName( configPassedToGetAccessoryNane );
165 |
166 | let expectedResult = "Kodi";
167 |
168 | assert.equal( name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult );
169 | assert.equal( config.name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult );
170 |
171 | // This fails with:
172 | // AssertionError: getAccessoryDisplayName should return displayName over name. result: Kodi expected: Kodi: expected 'Np' to equal 'Kodi'
173 | // + expected - actual
174 | // -Np
175 | // +Kodi
176 | assert.equal( config.Name, expectedResult, "getAccessoryDisplayName should return displayName over name. result: " + name + " expected: " + expectedResult );
177 | });
178 | })
179 | */
180 |
--------------------------------------------------------------------------------
/test/initPluginTest.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 |
4 |
5 | // ***************** TEST LOADING **********************
6 |
7 |
8 | var pluginModule = require( "../index" );
9 |
10 |
11 | describe(`Testing load of index.js`, ( ) =>
12 | {
13 | it( `index.js loaded should not be null`, ( ) =>
14 | {
15 | assert.isNotNull(pluginModule, `loading resulted in null` );
16 | });
17 |
18 | var t = typeof pluginModule.default;
19 | it( `index.js default initializer should be found`, ( ) =>
20 | {
21 | assert.equal(t, `function` );
22 | });
23 | });
24 |
25 | describe( `Testing homebridge API`, ( ) =>
26 | {
27 | it( `API should not be null`, ( ) =>
28 | {
29 | let apiInstance = new HomebridgeAPI();
30 | assert.isNotNull( apiInstance, ` apiInstance is null` );
31 | });
32 | });
33 |
34 | describe( `Testing homebridge setup`, ( ) =>
35 | {
36 | it( `HAP Categories should not be null`, ( ) =>
37 | {
38 | let apiInstance = new HomebridgeAPI();
39 | assert.isNotNull( apiInstance.hap.Categories, `Categories is null` );
40 | });
41 |
42 | it( `HAP Characteristic should be a function`, ( ) =>
43 | {
44 | let apiInstance = new HomebridgeAPI();
45 | assert.isFunction( apiInstance.hap.Characteristic, "Characteristic is not an function" );
46 | });
47 | it( `HAP Accessory should be a function`, ( ) =>
48 | {
49 | let apiInstance = new HomebridgeAPI();
50 | assert.isFunction( apiInstance.hap.Accessory, `apiInstance.hap.Accessory is not an function` );
51 | });
52 | it( `HAP Service should be a function`, ( ) =>
53 | {
54 | let apiInstance = new HomebridgeAPI();
55 | assert.isFunction( apiInstance.hap.Service, ` apiInstance.hap.Service is not an function` );
56 | });
57 | });
58 |
59 |
60 | // ***************** TEST Plugin Un Initialized Variables ***************
61 |
62 | describe( `Testing index.js plugin unInitialized variables.`, ( ) =>
63 | {
64 | it( `Plugin CMD4_DEVICE_TYPE_ENUM should be a object`, ( ) =>
65 | {
66 | assert.isObject(CMD4_DEVICE_TYPE_ENUM, `CMD4_DEVICE_TYPE_ENUM is not an object` );
67 | });
68 | it( `Plugin CMD4_ACC_TYPE_ENUM should be a object`, ( ) =>
69 | {
70 | assert.isObject(CMD4_ACC_TYPE_ENUM, "CMD4_ACC_TYPE_ENUM is not an object" );
71 | });
72 | it( `Plugin CMD4_ACC_TYPE_ENUM.EOL should be defined`, ( ) =>
73 | {
74 | assert.equal(CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL, `CMD4_DEVICE_TYPE_ENUM.EOL is incorrect` );
75 | });
76 | it( `Plugin CMD4_ACC_TYPE_ENUM.EOL should be defined`, ( ) =>
77 | {
78 | assert.equal(CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, "CMD4_ACC_TYPE_ENUM.EOL is incorrect" );
79 | });
80 | });
81 |
82 | // ***************** TEST Plugin Initialized Variables ***************
83 |
84 | describe( `Testing index.js plugin Initialized variables.`, ( ) =>
85 | {
86 | it( `Initialized Plugin CMD4_DEVICE_TYPE_ENUM.EOL should be correct`, ( ) =>
87 | {
88 | let apiInstance = new HomebridgeAPI();
89 | let cmd4 = pluginModule.default( apiInstance );
90 |
91 | assert.equal(cmd4.CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL, "returned CMD4_DEVICE_TYPE_ENUM.EOL is incorrect" );
92 | });
93 | it( `Initialized Plugin returned CMD4_ACC_TYPE_ENUM.EOL should be correct`, ( ) =>
94 | {
95 | let apiInstance = new HomebridgeAPI();
96 | let cmd4 = pluginModule.default( apiInstance );
97 |
98 | assert.equal(cmd4.CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, `returned CMD4_ACC_TYPE_ENUM.EOL is incorrect` );
99 | });
100 |
101 | it( `Initialized Plugin returned CMD4_DEVICE_TYPE_ENUM.properties length should be correct`, ( ) =>
102 | {
103 | let apiInstance = new HomebridgeAPI();
104 | let cmd4 = pluginModule.default( apiInstance );
105 | let properties = cmd4.CMD4_DEVICE_TYPE_ENUM.properties;
106 |
107 | assert.equal(Object.keys(properties).length, DEVICE_EOL, 'returned CMD4_DEVICE_TYPE_ENUM.properties length is incorrect' );
108 | });
109 |
110 | it( `Initialized Plugin returned CMD4_ACC_TYPE_ENUM.properties length should be correct`, ( ) =>
111 | {
112 | let apiInstance = new HomebridgeAPI();
113 | let cmd4 = pluginModule.default( apiInstance );
114 | let properties = cmd4.CMD4_ACC_TYPE_ENUM.properties;
115 |
116 | assert.equal(Object.keys(properties).length, ACC_EOL, 'returned CMD4_ACC_TYPE_ENUM.properties length is incorrect' );
117 | });
118 |
119 | it( `Initialized Plugin returned CMD4_ACC_TYPE_ENUM.properties[0-${ ACC_EOL }].Characteristic should be defined`, ( ) =>
120 | {
121 | let apiInstance = new HomebridgeAPI();
122 | let cmd4 = pluginModule.default( apiInstance );
123 | let properties = cmd4.CMD4_ACC_TYPE_ENUM.properties;
124 |
125 | for ( let accTypeEnumIndex = 0; accTypeEnumIndex < ACC_EOL; accTypeEnumIndex++ )
126 | {
127 | assert.isNotNull( properties[ accTypeEnumIndex ].characteristic, `Characteristic at index: ${ accTypeEnumIndex } is null. found ${ properties[ accTypeEnumIndex ].characteristic }` );
128 | }
129 | });
130 |
131 | it( `Initialized Plugin returned CMD4_DEVICE_TYPE_ENUM.properties[0-${ DEVICE_EOL }].service should be defined`, ( ) =>
132 | {
133 | let apiInstance = new HomebridgeAPI();
134 | let cmd4 = pluginModule.default( apiInstance );
135 | let properties = cmd4.CMD4_DEVICE_TYPE_ENUM.properties;
136 |
137 | for ( let deviceTypeEnumIndex = 0; deviceTypeEnumIndex < DEVICE_EOL; deviceTypeEnumIndex++ )
138 | {
139 | if ( properties[ deviceTypeEnumIndex ].deprecated == true )
140 | continue;
141 |
142 | assert.isNotNull( properties[ deviceTypeEnumIndex ].service, `service at index: ${ deviceTypeEnumIndex } is null.. found ${ properties[ deviceTypeEnumIndex ].service }` );
143 | }
144 | });
145 | /*
146 | // While this works individualy, it intercepts loading from other unit tests
147 | it( `Initialized Plugin returned CMD4_DEVICE_TYPE_ENUM.properties[0-${ DEVICE_EOL }].service should be defined`, ( ) =>
148 | {
149 | let Cmd4Platform = require( "../Cmd4Platform" ).Cmd4Platform;
150 |
151 |
152 | var apiInstance = new HomebridgeAPI(); // object we feed to Plugins
153 |
154 | let cmd4 = pluginModule.default( apiInstance );
155 | let CMD4_DEVICE_TYPE_ENUM = cmd4.CMD4_DEVICE_TYPE_ENUM;
156 | let CMD4_ACC_TYPE_ENUM = cmd4.CMD4_ACC_TYPE_ENUM;
157 | assert.equal( CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL, "returned CMD4_DEVICE_TYPE_ENUM.EOL is incorrect" );
158 | assert.equal( CMD4_ACC_TYPE_ENUM.EOL, ACC_EOL, `returned CMD4_ACC_TYPE_ENUM.EOL is incorrect` );
159 |
160 | let config =
161 | {
162 | "platform": "Cmd4",
163 | "name": "Cmd4",
164 | "accessories":
165 | [
166 | {
167 | "type": "Switch",
168 | "name": "PS_4",
169 | "displayName": "PS_4",
170 | "on": false,
171 | }
172 | ]
173 | }
174 |
175 | let cmd4Platform = new Cmd4Platform( null, config, apiInstance );
176 | let log = cmd4Platform.log;
177 | log.setBufferEnabled( );
178 |
179 |
180 | expect( cmd4Platform ).to.be.a.instanceOf( Cmd4Platform, "cmd4Platform is not an instance of Cmd4Platform" );
181 | assert.equal( log.logBuf, "", ` cmd4Platform unexpected output received: ${ log.logBuf }` );
182 | assert.equal( log.errBuf, "", ` cmd4Platform unexpected error output received: ${ log.errBuf }` );
183 | log.reset( );
184 |
185 |
186 | apiInstance.emit("didFinishLaunching");
187 |
188 | assert.include( log.logBuf, `Cmd4Platform didFinishLaunching`, `didFinishLaunching Incorrect stdout: ${ log.logBuf }` );
189 | assert.equal( log.errBuf, "", ` cmd4Platform Unexpected stderr: ${ log.errBuf }` );
190 |
191 | });
192 | */
193 | });
194 |
--------------------------------------------------------------------------------
/docs/AdvancedTroubleShooting.md:
--------------------------------------------------------------------------------
1 | # Homebridges-cmd4 - Advanced Trouble Shooting.
2 |
3 | ## Table of Contents
4 | * [**About Advanced Trouble Shooting**](#about-advanced-trouble-shooting)
5 | * [**The #1 Thing to Remember**](#the-1-thing-to-remember)
6 | * [**The Parameters sent by Cmd4**](#the-parameters-sent-by-cmd4)
7 | * [**Troubleshooting your own scripts**](#troubleshooting-your-own-scripts)
8 | * [***Create a middleWare shell script***](#create-a-middleware-shell-script)
9 | * [**Debug mode is your best friend**](#debug-mode-is-your-best-friend)
10 | * [**Debugging Fakegato history**](#debugging-fakegato-history)
11 | * [**Missing icons**](#missing-icons)
12 | * [**Child process error message**](#child-process-error-message)
13 | * [**License**](#license)
14 |
15 | ## About Advanced Trouble Shooting
16 | 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.
17 |
18 | ## The #1 Thing to Remember
19 | 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:
20 |
21 | ```bash
22 | *SHELL*> env -i bash --noprofile --norc
23 | ```
24 |
25 | From within this environment test your script like:
26 |
27 | ```bash
28 | *SHELL*> node .homebridge/YourScriptHere.js Get My_Fan On
29 | ```
30 |
31 | ### The Parameters Sent by Cmd4
32 | 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:
33 |
34 | ```
35 | Get < Accessory Name > < Characteristic >
36 | Set < Accessory Name > < Characteristic > < Value >
37 | ```
38 |
39 | ## Troubleshooting your own scripts
40 |
41 | ### Execute your script from the command line interface for *Get*
42 | Remembering that Cmd4 executes your script in a No environment setting. First execute your scripts from the CLI.
43 |
44 | ```bash
45 | *SHELL*> env -i bash --noprofile --norc
46 | *SHELL*> Get < Accessory Name > < Characteristic >
47 | ```
48 |
49 | The script must output a one word answer or multiple quoted words.
50 | Note: Your script must also exit with a 0 return code.
51 |
52 | ### Execute your script from the command line interface for *Set*
53 | Your script must respond to the Set command.
54 |
55 | ```bash
56 | *SHELL*> env -i bash --noprofile --norc
57 | *SHELL*> Set < Accessory Name > < Characteristic > < value >
58 | ```
59 |
60 | Note: Your script must also exit with a 0 return code.
61 |
62 | ### Debug mode is your best friend
63 | As with Basic Troubleshooting, if your script passes at the CLI, run homebridge in debug mode:
64 | 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.
65 | There are two ways to enable Cmd4 Debug logs.
66 |
67 | #### Method 1. Modifying the Cmd4 Platform section
68 | The Cmd4 Platform section can contain the enable Debug directive.
69 |
70 | ```json
71 | {
72 | "platform": "Cmd4",
73 | "name": "Cmd4",
74 | "debug": true
75 | }
76 | ```
77 |
78 | #### Method 2. Add DEBUG environental variable
79 |
80 | ```bash
81 | *SHELL*> DEBUG=Cmd4
82 | ```
83 | Note: For Homebridge-config-ui-x, you only need to write Cmd4 in the Environmental variable section.
84 |
85 |
86 | ### Create a middleWare shell script
87 | 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:
88 |
89 | ```
90 | #!/bin/bash
91 | echo $( date ) >> /tmp/Cmd4.log
92 | echo $* >> /tmp/Cmd4.log
93 | node .homebridge/Cmd4Scripts/State.js $* 2>&1 | tee -a /tmp/Cmd4.log
94 | ```
95 |
96 | Running a "tail -f /tmp/Cmd4.log" in a seperate terminal window will show you everything that is going on between the processes.
97 |
98 |
99 |
100 | ## Debugging Fakegato history
101 | See [fakegato-history](https://github.com/simont77/fakegato-history)
102 |
103 | if you have added fakegato history, but no history is showing, there are things you can check.
104 |
105 | ### Step 1. Check that the characteristic is polled.
106 | 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:
107 | ```json
108 | "polling": [{"characteristic": "currentHeatingCoolingState",
109 | "interval": 540, "timeout": 4000},
110 | {"characteristic": "currentTemperature",
111 | "interval": 60, "timeout": 4000}
112 | ],
113 | ```
114 |
115 | ### Step 2. Check the fakegato logs.
116 | The storagePath stores the history records. If there are no logs, run Cmd4 in Debug mode to see if the records are being created.
117 |
118 | ### Step 3. Check your fakegato configuration.
119 | 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.
120 |
121 | ```json
122 | "fakegato":{"eve":"thermo",
123 | "currentTemp": "currentTemperature",
124 | "setTemp": "targetTemperature",
125 | "valvePosition": "0",
126 | "storage": "fs",
127 | "storagePath": ".homebridge/FakegatoStorage",
128 | "folder": "folderName",
129 | "keyPath": "/place/to/store/my/keys/"
130 | }
131 | ```
132 |
133 | Note: The value "0" should be used for any characteristics value which is not possible to retrieve.
134 |
135 | ## Missing icons
136 | 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:
137 |
138 | ```json
139 | "category": "TELEVISION"
140 | ```
141 | This category only takes effect for Platform Accessories.
142 |
143 |
144 | ## Child process error message
145 | If you happen to see this error message:
146 | ```
147 | Error: Command failed: /homebridge/Server.sh Get 'Server' 'On'
148 |
149 | at ChildProcess.exithandler (child_process.js:297:12)
150 | at ChildProcess.emit (events.js:193:13)
151 | at maybeClose (internal/child_process.js:1001:16)
152 | at Process.ChildProcess._handle.onexit (internal/child_process.js:266:5)
153 | killed: true
154 | code: null
155 | signal: SIGTERM,
156 | cmd: "/homebridge/Server.sh Get Server On"
157 |
158 | ```
159 |
160 | The command may not exist, but also the timeout value in your config.json for that accessory may be too low.
161 |
162 |
163 | ## License
164 | See [LICENSE](https://github.com/ztalbot2000/homebridge-cmd4/blob/master/LICENSE)
165 |
166 |
167 |
168 |
171 |
172 | [homebridge]:https://github.com/nfarina/homebridge
173 | [ztalbot2000]:https://github.com/ztalbot2000
174 |
--------------------------------------------------------------------------------
/test/getSetAllValues.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const os = require( "os" );
4 | const cmd4StateDir = os.homedir( ) + "/.homebridge/Cmd4Scripts/Cmd4States/"
5 |
6 | var glob = require( "glob" );
7 |
8 |
9 | describe( 'Cleaning up any old Cmd4States/Status_Device_* files ...', ( ) =>
10 | {
11 | glob( cmd4StateDir + "Status_Device_*", null, function ( er, files )
12 | {
13 | for ( var file of files )
14 | {
15 | // To use the promise of unlink, it must be in an async function
16 | // so put it in one. Why not unLinkSync, because for whatever reason
17 | // some files were notbremoved synchronously.
18 | ( async( ) =>
19 | {
20 | await fs.unlink( file, function( err, result )
21 | {
22 | it('file:' + file +' should be removed', function ( done )
23 | {
24 | if ( err && err.code != 'ENOENT' )
25 | assert.isNull( err, 'file not removed err: ' + err + " result: " + result );
26 | done( );
27 | });
28 | });
29 | });
30 | }
31 | })
32 | });
33 |
34 |
35 |
36 | function removeStateFileThatShouldAlreadyBeRemoved( characteristicString )
37 | {
38 | let stateFile = cmd4StateDir + "Status_Device_" + characteristicString;
39 | if (fs.existsSync( stateFile ))
40 | {
41 | // console.log(' *** BLAST! The path exists.' + stateFile );
42 | // To use the promise of unlink, it must be in an async function
43 | // so put it in one. Why not unLinkSync, because for whatever reason
44 | // some files were notbremoved synchronously.
45 | ( async( ) =>
46 | {
47 | fs.unlink( stateFile, function( err, result )
48 | {
49 | if ( err && err.code != 'ENOENT' )
50 | assert.isNull( err, 'file not removed err: ' + err + " result: " + result );
51 | });
52 | });
53 | }
54 | }
55 |
56 |
57 | // ***************** TEST LOADING **********************
58 |
59 | // This would be the plugin un-initialized
60 | var pluginModule = require( "../index" );
61 |
62 | describe( "Testing load of index.js", ( ) =>
63 | {
64 | it( "index.js loaded should not be null", ( ) =>
65 | {
66 | assert.isNotNull( pluginModule, "loading resulted in null" );
67 | });
68 |
69 | var t = typeof pluginModule.default;
70 | it( "index.js default initializer should be found", ( ) =>
71 | {
72 | assert.equal( t, "function");
73 | });
74 | });
75 |
76 | // ************ TEST UNINITIALIZED CMD4_DEVICE_TYPE_ENUM EOL **************
77 | describe( "Quick Testing CMD4_DEVICE_TYPE_ENUM EOL", ( ) =>
78 | {
79 | it( "CMD4_DEVICE_TYPE_ENUM has EOL", ( ) =>
80 | {
81 | assert.isNotNull( pluginModule.DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM.EOL, "EOL is null" );
82 | });
83 |
84 | it( "CMD4_DEVICE_TYPE_ENUM.EOL = " + DEVICE_EOL, ( ) =>
85 | {
86 | assert.equal( pluginModule.DEVICE_DATA.CMD4_DEVICE_TYPE_ENUM.EOL, DEVICE_EOL );
87 | });
88 | });
89 |
90 | var _api = new HomebridgeAPI( ); // object we feed to Plugins
91 |
92 | describe( "Testing homebridge API", ( ) =>
93 | {
94 | it( "API should not be null", ( ) =>
95 | {
96 | assert.isNotNull( _api, "_api is null");
97 | });
98 | });
99 |
100 | describe( "Initializing our plugin module", ( ) =>
101 | {});
102 | var cmd4 = pluginModule.default( _api );
103 |
104 |
105 |
106 |
107 | // ***************** TEST State.js **********************
108 | const child_process = require( "child_process" );
109 |
110 |
111 | // *** TEST Get of Characteristic properties of default written data *******
112 | describe( "Testing State.js Get Characteristics default written data ( " + CMD4_ACC_TYPE_ENUM.EOL + " of them )", ( ) =>
113 | {
114 | for ( let accTypeEnumIndex=0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex ++ )
115 | {
116 | let characteristicString = cmd4.CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].type;
117 | let cmd = "./Extras/Cmd4Scripts/State.js";
118 | let args2 = " Get Device " + characteristicString + " ";
119 | let args = ["Get", "Device", characteristicString ];
120 |
121 | it( accTypeEnumIndex + ": " + cmd + args2 + "should return something", ( ) =>
122 | {
123 |
124 | // Why twice with two awaits, beats me !
125 | removeStateFileThatShouldAlreadyBeRemoved( characteristicString );
126 |
127 | const ps = child_process.spawnSync( cmd, args );
128 |
129 | var data="not set by me";
130 |
131 | if ( ps.status !== 0 )
132 | {
133 | assert.equal( ps.status, 0, "Process error stdout: " + ps.stdout + " stderr: " + ps.stderr + " status: " + ps.status + " signal: " + ps.signal );
134 | } else {
135 | data = ps.stdout;
136 | assert( data != "", "data returned '" + data + "'" );
137 |
138 | assert( data.length != 0, "data returned: " + data.length );
139 | }
140 | });
141 | }
142 | });
143 |
144 | // *** TEST Set of Characteristic properties *******
145 |
146 | let dummyData = "X0X0Test";
147 | let dummyDataR = "\"" + dummyData + "\"";
148 |
149 | describe( `Testing State.js Set Characteristics ( ${ CMD4_ACC_TYPE_ENUM.EOL } of them )`, ( ) =>
150 | {
151 | for ( let accTypeEnumIndex=0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex ++ )
152 | {
153 | let characteristicString = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].type;
154 | let cmd = "./Extras/Cmd4Scripts/State.js";
155 | let args2 = " Set Device " + characteristicString + " " + dummyData;
156 | let args = ["Set", "Device", characteristicString, dummyData];
157 |
158 | const ps = child_process.spawnSync( cmd, args );
159 |
160 | it( accTypeEnumIndex + ": " + cmd + args2 + " should return no data", ( ) =>
161 | {
162 | var data="not set by me";
163 |
164 | // console.log( "status is '%s'", ps.status );
165 | if ( ps.status !== 0 )
166 | {
167 | assert.equal( ps.status, 0, "Process error stdout: " + ps.stdout + " stderr: " + ps.stderr + " status: " + ps.status + " signal: " + ps.signal );
168 | } else {
169 | data = ps.stdout;
170 | assert( data == "", "No data should be returned: '" + data + "'" );
171 |
172 | }
173 | });
174 | }
175 | });
176 |
177 | // *** TEST Get of Characteristic properties after set of dummy data *******
178 |
179 | describe( "Begin Testing", ( ) =>
180 | {});
181 |
182 |
183 | // *** TEST Get of Characteristic properties of written data *******
184 | describe( "Testing State.js Get Characteristics written data ( " + cmd4.CMD4_ACC_TYPE_ENUM.EOL + " of them ). Note: May fail even number of times ????", ( ) =>
185 | {
186 | for ( let accTypeEnumIndex=0; accTypeEnumIndex < CMD4_ACC_TYPE_ENUM.EOL; accTypeEnumIndex ++ )
187 | {
188 | let characteristicString = CMD4_ACC_TYPE_ENUM.properties[accTypeEnumIndex].type;
189 | let cmd = "./Extras/Cmd4Scripts/State.js";
190 | let args2 = " Get Device " + characteristicString + " ";
191 | let args = ["Get", "Device", characteristicString];
192 |
193 | it( accTypeEnumIndex + ": " + cmd + args2 + "should return: " + dummyDataR , ( ) =>
194 | {
195 | const ps = child_process.spawnSync( cmd, args );
196 |
197 | var data="not set by me";
198 |
199 | if ( ps.status !== 0 )
200 | {
201 | assert.equal( ps.status, 0, "Process error stdout: " + ps.stdout + " stderr: " + ps.stderr + " status: " + ps.status + " signal: " + ps.signal );
202 | } else {
203 | data = ps.stdout;
204 | // fixme ??? assert( data == dummyData, "data returned: '" + data + "'" );
205 | // The carriage return is stripped by getValue
206 | assert( data == dummyDataR, "data returned:" + data );
207 |
208 | assert( data.length != 0, "data returned: " + data.length );
209 | }
210 | });
211 | }
212 | });
213 |
--------------------------------------------------------------------------------
/cmd4Constants.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Naming convention
4 | // DEFAULT_ => Default values
5 | // _l => Lower Case
6 | // _lv => Lower Case Variable of same name
7 |
8 | // Triggers which Array CMD4Accessory will be placed
9 | // Either cmd4Accessories or cmd4StandaloneAccessories
10 | //
11 |
12 |
13 | var cmd4Constants = {
14 | STANDALONE: "Standalone",
15 | PLATFORM: "Platform",
16 |
17 | // Default intervals
18 | // 10 seconds
19 | SLOW_STATE_CHANGE_RESPONSE_TIME: 10000,
20 | // 3 seconds
21 | MEDIUM_STATE_CHANGE_RESPONSE_TIME: 3000,
22 | // 3 seconds
23 | DEFAULT_STATE_CHANGE_RESPONSE_TIME: 3000,
24 | // 1 second
25 | FAST_STATE_CHANGE_RESPONSE_TIME: 1000,
26 |
27 | // 1 minute
28 | DEFAULT_INTERVAL: 60000,
29 | // 1 minute
30 | DEFAULT_TIMEOUT: 60000,
31 |
32 | // Not a Bool, otherwise conditional check fails
33 | DEFAULT_STATUSMSG: "TRUE",
34 |
35 | DEFAULT_QUEUE_TYPE: "WoRm",
36 | // 10 seconds
37 | DEFAULT_QUEUE_PAUSE_TIMEOUT: 10000,
38 |
39 | // No debug
40 | DEFAULT_DEBUG: false,
41 | // No funny TLV8 characteristics
42 | DEFAULT_ALLOW_TLV8: false,
43 | DEFAULT_OUTPUTCONSTANTS: false,
44 |
45 | // Fakegato Constants
46 | FAKEGATO_TYPE_ENERGY: "energy",
47 | FAKEGATO_TYPE_ROOM: "room",
48 | FAKEGATO_TYPE_WEATHER: "weather",
49 | FAKEGATO_TYPE_DOOR: "door",
50 | FAKEGATO_TYPE_MOTION: "motion",
51 | FAKEGATO_TYPE_THERMO: "thermo",
52 | FAKEGATO_TYPE_AQUA: "aqua",
53 | EVE: "eve",
54 | STORAGE: "storage",
55 | STORAGEPATH: "storagePath",
56 | FOLDER: "folder",
57 | KEYPATH: "keyPath",
58 | STATUS: "status",
59 | TEMP: "temp",
60 | SETTEMP: "setTemp",
61 | HUMIDITY: "humidity",
62 | PPM: "ppm",
63 | POWER: "power",
64 | PRESSURE: "pressure",
65 | CURRENTTEMP: "currentTemp",
66 | VALVEPOSITION: "valvePosition",
67 | WATERAMOUNT: "waterAmount",
68 | TIME: "time",
69 | PATH: "path",
70 |
71 | FS: "fs",
72 | GOOGLE_DRIVE: "googleDrive",
73 |
74 | // Config Constants
75 | DEBUG: "debug",
76 | OUTPUTCONSTANTS: "outputConstants",
77 | STATUSMSG: "statusMsg",
78 | QUEUETYPE: "queueType",
79 | QUEUETYPES: "queueTypes",
80 |
81 | // Queue Types
82 | QUEUETYPE_SEQUENTIAL: "Sequential",
83 | QUEUETYPE_WORM: "WoRm",
84 | QUEUETYPE_WORM2: "WoRm2",
85 | // Used internally to mean only polled entries go straight through the queue
86 | QUEUETYPE_STANDARD: "StandarD",
87 | // Used internally to mean entries go straight through the queue
88 | QUEUETYPE_PASSTHRU: "None",
89 | DEFAULT_STANDARD_QUEUE_RETRY_COUNT: 0,
90 | DEFAULT_WORM_QUEUE_RETRY_COUNT: 0,
91 | QUEUE_RETRIES: "retries",
92 |
93 |
94 |
95 | // Platform/Accessory Config Constants
96 | TYPE: "type",
97 | SUBTYPE: "subType",
98 | DISPLAYNAME: "displayName",
99 | UUID: "uuid",
100 | ACCESSORY: "accessory",
101 | CATEGORY: "category",
102 | PUBLISHEXTERNALLY: "publishExternally",
103 | CHARACTERISTIC: "characteristic",
104 | TIMEOUT: "timeout",
105 | QUEUE: "queue",
106 | POLLING: "polling",
107 | INTERVAL: "interval",
108 | STATECHANGERESPONSETIME: "stateChangeResponseTime",
109 | STATE_CMD_PREFIX: "state_cmd_prefix",
110 | STATE_CMD_SUFFIX: "state_cmd_suffix",
111 | STATE_CMD: "state_cmd",
112 | FAKEGATO: "fakegato",
113 | REQUIRES: "requires",
114 | CONSTANTS: "constants",
115 | VARIABLES: "variables",
116 | LINKEDTYPES: "linkedTypes",
117 | ACCESSORIES: "accessories",
118 | URL: "url",
119 | ALLOWTLV8: "allowTLV8",
120 |
121 | DEFINITIONS: "definitions",
122 | PROPS: "props",
123 |
124 | // While also characteristics, they are also used by
125 | // the infomation service
126 | MANUFACTURER: "manufacturer",
127 | SERIALNUMBER: "serialNumber",
128 | MODEL: "model",
129 |
130 | // Internal list variables
131 | ACCESSORY_lv: "accessory",
132 | CHARACTERISTIC_STRING_lv: "characteristicString",
133 | CALLBACK_lv: "callback",
134 | ACC_TYPE_ENUM_INDEX_lv: "accTypeEnumIndex",
135 | INTERVAL_lv: "interval",
136 | IS_SET_lv: "isSet",
137 | QUEUE_NAME_lv: "queueName",
138 | QUEUE_GET_IS_UPDATE_lv: "queueGetIsUpdate",
139 | STATE_CHANGE_RESPONSE_TIME_lv: "stateChangeResponseTime",
140 | TIMEOUT_lv: "timeout",
141 | VALUE_lv: "value",
142 | CMD4_STORAGE_lv: "cmd4Storage",
143 |
144 | ERROR_STRING_MIN: -151,
145 | ERROR_TIMER_EXPIRED: -151,
146 | // ERROR_CMD_FAILED_REPLY: -152,
147 | ERROR_NULL_REPLY: -153,
148 | ERROR_NULL_STRING_REPLY: -154,
149 | ERROR_EMPTY_STRING_REPLY: -155,
150 | ERROR_2ND_NULL_STRING_REPLY: -156,
151 | ERROR_NON_CONVERTABLE_REPLY: -157,
152 | ERROR_NO_DATA_REPLY: -158,
153 | ERROR_STRING_MAX: -158,
154 | ERROR_STRINGS:
155 | [ // cmd4Constants.ERROR_TIMER_EXPIRED -151
156 | "Timer expired contacting accessory",
157 | // cmd4Constants.ERROR_CMD_FAILED_REPLY -152
158 | "Command failed",
159 | // cmd4Constants.ERROR_NULL_REPLY -153
160 | "Reply is NULL",
161 | // cmd4Constants.ERROR_NULL_STRING_REPLY -154
162 | "Reply is NULL string",
163 | // cmd4Constants.ERROR_EMPTY_STRING_REPLY -155
164 | "Reply is an empty string",
165 | // cmd4Constants.ERROR_2ND_NULL_STRING_REPLY -156
166 | "Reply is still NULL",
167 | // cmd4Constants.ERROR_NON_CONVERTABLE_REPLY -157
168 | "Cannot convert characteristic string",
169 | // cmd4Constants.ERROR_NO_DATA_REPLY -158
170 | "No data returned from accessory"
171 | ],
172 |
173 | // Convert our known Error Codes to strings
174 | errorString: function( index )
175 | {
176 | let offset = - index + cmd4Constants.ERROR_STRING_MIN ;
177 | let max = cmd4Constants.ERROR_STRING_MIN - cmd4Constants.ERROR_STRING_MAX;
178 |
179 | //console.log(" index is " + index );
180 | //console.log(" offset is " + offset );
181 | //console.log(" max is " + max );
182 | // i.e 0-7
183 | if ( index == 0 )
184 | return "Device returned SUCCESS; " + index;
185 |
186 | //if ( offset < 0 || offset > max )
187 | if ( index > cmd4Constants.ERROR_STRING_MIN )
188 | return "Device returned its own error; " + index;
189 | if ( index < cmd4Constants.ERROR_STRING_MAX )
190 | return "Device returned its own error; " + index;
191 |
192 | // Should not happen because of the above checks
193 | if ( offset < 0 )
194 | return "Invalid Error min index: " + index;
195 |
196 | // Should not happen because of the above checks
197 | if ( offset > max )
198 | return "Invalid Error max index: " + index;
199 |
200 | return cmd4Constants.ERROR_STRINGS[ offset ];
201 | },
202 |
203 |
204 |
205 | // Static Messages
206 | DBUSY: "Perhaps your device is busy or unavailable?"
207 | };
208 | module.exports = cmd4Constants;
209 |
--------------------------------------------------------------------------------
/test/Logger.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Settings, Globals and Constants
4 | let settings = require( "../cmd4Settings" );
5 | // Let logger control logs for Unit Testing
6 | settings.cmd4Dbg = true;
7 |
8 | var _api = new HomebridgeAPI( ); // object we feed to Plugins
9 |
10 | // Init the library for all to use
11 | let CMD4_ACC_TYPE_ENUM = ACC_DATA.init( _api.hap.Characteristic, _api.hap.Formats, _api.hap.Units, _api.hap.Perms );
12 | let CMD4_DEVICE_TYPE_ENUM = DEVICE_DATA.init( CMD4_ACC_TYPE_ENUM, _api.hap.Service, _api.hap.Characteristic, _api.hap.Categories );
13 |
14 |
15 | let { Cmd4Accessory } = require( "../Cmd4Accessory" );
16 |
17 |
18 | // ******** QUICK TEST CMD4_ACC_TYPE_ENUM *************
19 | describe( "Quick Test load of CMD4_ACC_TYPE_ENUM", ( ) =>
20 | {
21 | it( "CMD4_ACC_TYPE_ENUM.EOL =" + ACC_EOL, ( ) =>
22 | {
23 | expect( CMD4_ACC_TYPE_ENUM.EOL ).to.equal( ACC_EOL );
24 | });
25 | });
26 |
27 |
28 | // ******** QUICK TEST CMD4_DEVICE_TYPE_ENUM *************
29 | describe( "Quick Test load of CMD4_DEVICE_TYPE_ENUM", ( ) =>
30 | {
31 | it( "CMD4_DEVICE_TYPE_ENUM.EOL =" + DEVICE_EOL, ( ) =>
32 | {
33 | expect( CMD4_DEVICE_TYPE_ENUM.EOL ).to.equal( DEVICE_EOL );
34 | });
35 | });
36 |
37 |
38 | // ******** TEST logger *************
39 | describe('A simple logger Test', ( ) =>
40 | {
41 | it( "Test bufferEnabled is off by default", ( ) =>
42 | {
43 | const log = new Logger( );
44 | log.setOutputEnabled( false );
45 |
46 | let STDOUT_DATA="stdout_data";
47 | log.info( STDOUT_DATA );
48 |
49 | // Logger adds a \n so use include
50 | assert.equal( log.logBuf , "", `Expected no logs to stdout` );
51 | assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` );
52 | assert.equal( log.errBuf , "", `Expected no logs to stderr` );
53 | assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` );
54 |
55 | });
56 |
57 | it( "Test info log to stdout gets captured and mothing else", ( ) =>
58 | {
59 | const log = new Logger( );
60 | log.setBufferEnabled( true );
61 | log.setOutputEnabled( false );
62 | log.setDebugEnabled( false );
63 |
64 | let STDOUT_DATA="stdout_data";
65 | log.info( STDOUT_DATA );
66 |
67 | // Logger adds a \n so use include
68 | assert.include( log.logBuf , STDOUT_DATA, `Expected logs to stdout for log.info ` );
69 | assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` );
70 | assert.equal( log.errBuf , "", `Expected no logs to stderr` );
71 | assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` );
72 |
73 | });
74 |
75 | it( "Test warn log to stderr gets captured and mothing else", ( ) =>
76 | {
77 | const log = new Logger( );
78 | log.setBufferEnabled( true );
79 | log.setOutputEnabled( false );
80 | log.setDebugEnabled( false );
81 |
82 | let STDERR_DATA="stderr_data";
83 | log.warn( STDERR_DATA );
84 |
85 | assert.equal( log.logBuf , "", `Expected no logs to stout` );
86 | assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` );
87 | // Logger adds a \n so use include
88 | assert.include( log.errBuf , STDERR_DATA, `Expected logs to stderr for log.warn ` );
89 | assert.equal( log.errLineCount, 1 , `unexpected number of lines to stderr` );
90 |
91 | });
92 |
93 | it( "Test log to stderr gets captured and mothing else", ( ) =>
94 | {
95 | const log = new Logger( );
96 | log.setBufferEnabled( true );
97 | log.setOutputEnabled( false );
98 | log.setDebugEnabled( false );
99 |
100 | let STDERR_DATA="stderr_data";
101 | log.error( STDERR_DATA );
102 |
103 | assert.equal( log.logBuf , "", `Expected no logs to stdout` );
104 | assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` );
105 | // Logger adds a \n so use include
106 | assert.include( log.errBuf , STDERR_DATA, `Expected logs to stderr` );
107 | assert.equal( log.errLineCount, 1 , `unexpected number of lines to stderr` );
108 |
109 | });
110 |
111 | it( "Test log to stderr and different to stdout", ( ) =>
112 | {
113 | const log = new Logger( );
114 | log.setBufferEnabled( true );
115 | log.setOutputEnabled( false );
116 | log.setDebugEnabled( false );
117 |
118 | let STDERR_DATA="stderr_data";
119 | let STDOUT_DATA="stdout_data";
120 | log.info( STDOUT_DATA );
121 | log.error( STDERR_DATA );
122 |
123 | // Logger adds a \n so use include
124 | assert.include( log.logBuf , STDOUT_DATA, `Expected logs to stdout for log.info ` );
125 | assert.equal( log.logLineCount, 1 , `unexpected number of lines to stdout` );
126 | // Logger adds a \n so use include
127 | assert.include( log.errBuf , STDERR_DATA, `Expected logs to stderr` );
128 | assert.equal( log.errLineCount, 1 , `unexpected number of lines to stderr` );
129 |
130 | });
131 |
132 |
133 | it( "Test can create an instance of Cmd4Accessory with new logger", ( ) =>
134 | {
135 | let config =
136 | {
137 | name: "Test Switch",
138 | type: "Switch",
139 | on: false,
140 | polling: true,
141 | state_cmd: "./test/echoScripts/echo_1"
142 | };
143 |
144 | const log = new Logger( );
145 | log.setBufferEnabled( true );
146 | log.setOutputEnabled( false );
147 | log.setDebugEnabled( false );
148 |
149 |
150 | let accessory = new Cmd4Accessory( log, config, _api, [ ] );
151 |
152 | assert.instanceOf( accessory , Cmd4Accessory, "Expected accessory to be instance of Cmd4Accessory. Found %s" , accessory );
153 | assert.equal( log.logBuf , "", `unexpected logs to stdout for a simple instance of Cmd4Accessory` );
154 | assert.equal( log.logLineCount, 0 , `unexpected number of lines to stdout` );
155 | assert.equal( log.errBuf , "", `Expected no logs to stderr for a simple instance of Cmd4Accessory` );
156 | assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` );
157 |
158 | log.reset();
159 | });
160 |
161 | it( "Test can create an instance of Cmd4Accessory with a debug log", ( ) =>
162 | {
163 | let config =
164 | {
165 | name: "Test Switch",
166 | type: "Switch",
167 | on: false,
168 | polling: true,
169 | state_cmd: "./test/echoScripts/echo_1"
170 | };
171 |
172 | let log = new Logger( );
173 | log.setBufferEnabled( true );
174 | log.setOutputEnabled( false );
175 | log.setDebugEnabled( true );
176 |
177 |
178 | new Cmd4Accessory( log, config, _api, [ ] );
179 |
180 | assert.include( log.logBuf, "Creating Standalone Accessory type for ", `Expected debug logs to stdout with setDebugEnabled` );
181 | //assert.equal( log.logLineCount, 17 , `unexpected number of lines to stdout` );
182 | assert.equal( log.errBuf, "", `Expected no logs to stderr for a simple instance of Cmd4Accessory` );
183 | assert.equal( log.errLineCount, 0 , `unexpected number of lines to stderr` );
184 |
185 | log.reset();
186 | });
187 |
188 | it( "Test logger performance of NOT enabled message", ( ) =>
189 | {
190 | const log = new Logger( );
191 | log.setBufferEnabled( false );
192 | log.setOutputEnabled( false );
193 | log.setDebugEnabled( false );
194 | let entry = { characteristicString: "testCharacteristic",
195 | accessory:
196 | { displayName: "testDevice",
197 | queue:
198 | { inProgressGets: 0,
199 | inProgressSets: 0
200 | }
201 | }
202 | };
203 |
204 | let logStartTime = process.hrtime( );
205 |
206 | 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 }` );
207 | let logTotalTime = process.hrtime( logStartTime );
208 |
209 | let debug = false;
210 | let debugStartTime = process.hrtime( );
211 | 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 }` );
212 | let debugTotalTime = process.hrtime( debugStartTime );
213 |
214 | let diff = logTotalTime[1] - debugTotalTime[1];
215 | console.log( `logTotalTime: ${ logTotalTime[1] } debugTotalTime: ${ debugTotalTime[1] } diff: ${ diff }` );
216 |
217 | assert.isAbove( logTotalTime[1], debugTotalTime[1], `Expected log total time to be greater than debug total time` );
218 |
219 | });
220 | });
221 |
222 |
--------------------------------------------------------------------------------