diff --git a/.gitignore b/.gitignore index 91fce81..15f9d27 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ test2/.uix-secrets test2/auth.json test2/config.json.* test2/persist/ControllerStorage.*.json +test2/backups/* diff --git a/README.md b/README.md index 77cffcb..aae852a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ ## Compatibility with previous versions +**From version 1.1.33, MQTT-Thing requires Node.js 14 or later.** + **From version 1.1.x, raw JavaScript values for Boolean properties are passed to MQTT apply functions.** This may change published message formats, e.g. when apply functions are used to build JSON strings. For full details of changes please see the [Release notes](docs/ReleaseNotes.md). diff --git a/config.schema.json b/config.schema.json index 3871950..4e740ca 100644 --- a/config.schema.json +++ b/config.schema.json @@ -536,6 +536,20 @@ "condition": { "functionBody": "return ['weatherStation'].includes(model.type);" } + }, + "getmaxWind": { + "type": "string", + "description": "Topic used to notify mqttthing of 'wind speed [km/h]' (optional, Eve-only)", + "condition": { + "functionBody": "return ['weatherStation'].includes(model.type);" + } + }, + "getDewPoint": { + "type": "string", + "description": "Topic used to notify mqttthing of 'DewPoint' (optional, Eve-only)", + "condition": { + "functionBody": "return ['weatherStation'].includes(model.type);" + } }, "getActive": { "type": "string", diff --git a/docs/Codecs.md b/docs/Codecs.md index 9caee7f..34da757 100644 --- a/docs/Codecs.md +++ b/docs/Codecs.md @@ -251,7 +251,7 @@ This section lists the properties available for each accessory type. All accesso ### Weather Station -`currentTemperature`, `statusActive`, `statusFault`, `statusTampered`, `statusLowBattery`, `currentRelativeHumidity`, `airPressure`, `weatherCondition`, `rain1h`, `rain24h`, `uvIndex`, `visibility`, `windDirection`, `windSpeed` +`currentTemperature`, `statusActive`, `statusFault`, `statusTampered`, `statusLowBattery`, `currentRelativeHumidity`, `airPressure`, `weatherCondition`, `rain1h`, `rain24h`, `uvIndex`, `visibility`, `windDirection`, `windSpeed`, `maxwindSpeed`, `Dewpoint` ### Window diff --git a/docs/Configuration.md b/docs/Configuration.md index 861967e..c886af5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -227,7 +227,7 @@ Avoid the use of "/" in characteristics of the Information Service (e.g. serial ### Confirmation -Some accessories support confirmation for some of their 'set' topics. When enabled by configuring `confirmationPeriodms`, the accessory *must* echo anything sent to appropriate `setX` subject(s) to the corresponding `getX` subject(s). Where homebridge-mqttthing doesn't see a confirmation within the configured configuration period (specified in milliseconds), it will publish the set message again. Messages will be republished up to 3 times by default, but this can be changed by also specifying `retryLimit`. +Some accessories support confirmation for some of their 'set' topics. When enabled by configuring `confirmationPeriodms`, the accessory *must* echo anything sent to appropriate `setX` subject(s) to the corresponding `getX` subject(s). Where homebridge-mqttthing doesn't see a confirmation within the configured confirmation period (specified in milliseconds), it will publish the set message again. Messages will be republished up to 3 times by default, but this can be changed by also specifying `retryLimit`. Accessories supporting message confirmation list the topics supporting message confirmation below. @@ -308,4 +308,4 @@ Any settings which apply to all services may be defined within the custom-type a Custom accessories are only intended for use with simple services, not with accessories like 'weather station' which already combine multiple services. -Custom accessories cannot be configured through Config UI X. \ No newline at end of file +Custom accessories cannot be configured through Config UI X. diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index b6238ea..0c32256 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -5,6 +5,19 @@ # Homebridge MQTT-Thing: Release Notes +### Version 1.1.33 ++ Revert change in 1.1.32. MQTT-Thing now requires Node.js 14 or later. ++ Engines updated to require Homebridge 1.3.5 and Node.js 14 (thanks, Donavan Becker) ++ Fixed typo in documentation (thanks, Brian White) ++ Fixed duration characteristic validation error message (thanks, Thomas Vandahl) ++ Moved codec loading earlier to allow codecs to manipulate the configuration (thanks, Martin) ++ Added water level characteristic to leak sensor (thanks, Moritz) ++ Added max wind and dewpoint characteristics to weather station (thanks, 2610) ++ Added jsonpath support (thanks, Antonio Yip) + +### Version 1.1.32 ++ Improve compatibility with older Node.js versions + ### Version 1.1.31 + Improve null handling (multicharacteristic) (thanks, Jakub Samek) + Added optimizePublishing option diff --git a/index.js b/index.js index aae24c2..f894f64 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ var homebridgeLib = require( 'homebridge-lib' ); var fakegatoHistory = require( 'fakegato-history' ); var fs = require( "fs" ); var path = require( "path" ); +var jp = require( "jsonpath" ); var mqttlib = require( './libs/mqttlib' ); const EventEmitter = require( 'events' ); @@ -46,8 +47,28 @@ function makeThing( log, accessoryConfig, api ) { } // MQTT Subscribe - function mqttSubscribe( topic, property, handler ) { - mqttlib.subscribe( ctx, topic, property, handler ); + function mqttSubscribe( topicWithJsonpath, property, handler ) { + let jsonpathIndex = topicWithJsonpath.indexOf('$'); + let topic = jsonpathIndex > 0 ? topicWithJsonpath.substring(0, jsonpathIndex) : topicWithJsonpath + let query = topicWithJsonpath.substring(jsonpathIndex); + + mqttlib.subscribe( ctx, topic, property, function( topic, message ) { + log.debug(topic, String(message)); + + if (jsonpathIndex > 0 && query.length > 0) { + try { + let json = JSON.parse(message); + let values = jp.query(json, query); + log.debug(topic, query, values); + handler(topic, values.shift()); + } catch(error) { + log.error(topic, "error:", error, "message:", message); + handler(topic, message); + } + } else { + handler(topic, message); + } + }); } // MQTT Publish @@ -1691,6 +1712,18 @@ function makeThing( log, accessoryConfig, api ) { floatCharacteristic( service, 'windSpeed', Eve.Characteristics.WindSpeed, null, config.topics.getWindSpeed, 0 ); } + // Characteristic.maxWind (Eve-only) + function characteristic_MaximumWindSpeed( service ) { + service.addOptionalCharacteristic( Eve.Characteristics.MaximumWindSpeed ); // to avoid warnings + floatCharacteristic( service, 'maxWind', Eve.Characteristics.MaximumWindSpeed, null, config.topics.getmaxWind, 0 ); + } + + // Characteristic.Dewpoint(Eve-only) + function characteristic_DewPoint( service ) { + service.addOptionalCharacteristic( Eve.Characteristics.DewPoint ); // to avoid warnings + floatCharacteristic( service, 'DewPoint', Eve.Characteristics.DewPoint, null, config.topics.getDewPoint, 0 ); + } + // Characteristic.ContactSensorState function characteristic_ContactSensorState( service ) { booleanCharacteristic( service, 'contactSensorState', Characteristic.ContactSensorState, @@ -1995,6 +2028,12 @@ function makeThing( log, accessoryConfig, api ) { }, undefined, config.resetStateAfterms ); } + // Characteristic.WaterLevel + function characteristic_WaterLevel( service ) { + let options = { minValue: 0, maxValue: 100 } + integerCharacteristic( service, 'waterLevel', Characteristic.WaterLevel, config.topics.setWaterLevel, config.topics.getWaterLevel, options); + } + // Characteristic.TargetPosition function characteristic_TargetPosition( service ) { integerCharacteristic( service, 'targetPosition', Characteristic.TargetPosition, config.topics.setTargetPosition, config.topics.getTargetPosition ); @@ -2450,11 +2489,11 @@ function makeThing( log, accessoryConfig, api ) { } if( !topic_setDuration ) { /* no topic specified, but propery is still created internally */ - addCharacteristic( service, property_setDuration, Characteristic.SetDuration, 30, function() { + addCharacteristic( service, property_setDuration, Characteristic.SetDuration, 1200, function() { log.debug( 'set "' + property_setDuration + '" to ' + state[ property_setDuration ] + 's.' ); } ); } else { - integerCharacteristic( service, property_setDuration, Characteristic.SetDuration, topic_setDuration, topic_getDuration, { initialValue: 30 } ); + integerCharacteristic( service, property_setDuration, Characteristic.SetDuration, topic_setDuration, topic_getDuration, { initialValue: 1200 } ); } // minimum/maximum duration if( config.minDuration !== undefined || config.maxDuration !== undefined ) { @@ -2836,6 +2875,14 @@ function makeThing( log, accessoryConfig, api ) { if( config.topics.getWindSpeed ) { characteristic_WindSpeed( weatherSvc ); addWeatherSvc = true; + } + if( config.topics.getmaxWind ) { + characteristic_MaximumWindSpeed( weatherSvc ); + addWeatherSvc = true; + } + if( config.topics.getDewPoint ) { + characteristic_DewPoint( weatherSvc ); + addWeatherSvc = true; } if( addWeatherSvc ) { services.push( weatherSvc ); @@ -2968,6 +3015,9 @@ function makeThing( log, accessoryConfig, api ) { } else if( configType == "leakSensor" ) { service = new Service.LeakSensor( name, subtype ); characteristic_LeakDetected( service ); + if( config.topics.setWaterLevel || config.topics.getWaterLevel ) { + characteristic_WaterLevel( service ); + } addSensorOptionalCharacteristics( service ); } else if( configType == "microphone" ) { service = new Service.Microphone( name, subtype ); diff --git a/libs/mqttlib.js b/libs/mqttlib.js index 326f568..7be89d3 100644 --- a/libs/mqttlib.js +++ b/libs/mqttlib.js @@ -58,6 +58,52 @@ var mqttlib = new function() { let logmqtt = config.logMqtt; var clientId = 'mqttthing_' + config.name.replace(/[^\x20-\x7F]/g, "") + '_' + Math.random().toString(16).substr(2, 8); + // Load any codec + if( config.codec ) { + let codecPath = makeCodecPath( config.codec, ctx.homebridgePath ); + if( fs.existsSync( codecPath ) ) { + // load codec + log( 'Loading codec from ' + codecPath ); + let codecMod = require( codecPath ); + if( typeof codecMod.init === "function" ) { + + // direct publishing + let directPub = function( topic, message ) { + optimizedPublish( topic, message, ctx ); + }; + + // notification by property + let notifyByProp = function( property, message ) { + let handlers = propDispatch[ property ]; + if( handlers ) { + for( let i = 0; i < handlers.length; i++ ) { + handlers[ i ]( '_prop-' + property, message ); + } + } + }; + + // initialise codec + let codec = ctx.codec = codecMod.init( { log, config, publish: directPub, notify: notifyByProp } ); + if( codec ) { + // encode/decode must be functions + if( typeof codec.encode !== "function" ) { + log.warn( 'No codec encode() function' ); + codec.encode = null; + } + if( typeof codec.decode !== "function" ) { + log.warn( 'No codec decode() function' ); + codec.decode = null; + } + } + } else { + // no initialisation function + log.error( 'ERROR: No codec initialisation function returned from ' + codecPath ); + } + } else { + log.error( 'ERROR: Codec file [' + codecPath + '] does not exist' ); + } + } + // start with any configured options object var options = config.mqttOptions || {}; @@ -143,52 +189,6 @@ var mqttlib = new function() { } }); - // Load any codec - if( config.codec ) { - let codecPath = makeCodecPath( config.codec, ctx.homebridgePath ); - if( fs.existsSync( codecPath ) ) { - // load codec - log( 'Loading codec from ' + codecPath ); - let codecMod = require( codecPath ); - if( typeof codecMod.init === "function" ) { - - // direct publishing - let directPub = function( topic, message ) { - optimizedPublish( topic, message, ctx ); - }; - - // notification by property - let notifyByProp = function( property, message ) { - let handlers = propDispatch[ property ]; - if( handlers ) { - for( let i = 0; i < handlers.length; i++ ) { - handlers[ i ]( '_prop-' + property, message ); - } - } - }; - - // initialise codec - let codec = ctx.codec = codecMod.init( { log, config, publish: directPub, notify: notifyByProp } ); - if( codec ) { - // encode/decode must be functions - if( typeof codec.encode !== "function" ) { - log.warn( 'No codec encode() function' ); - codec.encode = null; - } - if( typeof codec.decode !== "function" ) { - log.warn( 'No codec decode() function' ); - codec.decode = null; - } - } - } else { - // no initialisation function - log.error( 'ERROR: No codec initialisation function returned from ' + codecPath ); - } - } else { - log.error( 'ERROR: Codec file [' + codecPath + '] does not exist' ); - } - } - ctx.mqttClient = mqttClient; return mqttClient; }; @@ -250,6 +250,9 @@ var mqttlib = new function() { let decoded; try { decoded = applyFn( message, getApplyState( ctx, property ) ); + if( config.logMqtt ) { + log( 'apply() function decoded message to [' + decoded + ']' ); + } } catch( ex ) { log( 'Decode function apply( message) { ' + extendedTopic.apply + ' } failed for topic ' + topic + ' with message ' + message + ' - ' + ex ); } diff --git a/package-lock.json b/package-lock.json index f181835..ca715c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "fakegato-history": "^0.6.2", "homebridge-lib": "~5.1.4", + "jsonpath": "^1.1.1", "mqtt": "^4.2.6" }, "engines": { @@ -338,6 +339,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -450,6 +456,67 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha1-dqD9Zvz+FU/SkmZ9wmQBl1CxZXs=", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -476,6 +543,11 @@ "node": ">=4.3.2" } }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, "node_modules/fast-text-encoding": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", @@ -1062,6 +1134,16 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, + "node_modules/jsonpath": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", + "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", + "dependencies": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + } + }, "node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -1089,6 +1171,18 @@ "node": ">=0.10.0" } }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1303,6 +1397,22 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", @@ -1324,6 +1434,14 @@ "node": ">=0.10.0" } }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -1466,6 +1584,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -1487,6 +1614,14 @@ "node": ">= 6" } }, + "node_modules/static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "dependencies": { + "escodegen": "^1.8.1" + } + }, "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -1575,6 +1710,17 @@ "node": ">=0.10.0" } }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -1602,6 +1748,11 @@ "node": ">=0.10.0" } }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, "node_modules/unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", @@ -1678,6 +1829,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1961,6 +2120,11 @@ "which-typed-array": "^1.1.2" } }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2052,6 +2216,40 @@ "is-symbol": "^1.0.2" } }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, + "esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha1-dqD9Zvz+FU/SkmZ9wmQBl1CxZXs=" + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -2071,6 +2269,11 @@ "googleapis": ">39.1.0" } }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, "fast-text-encoding": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz", @@ -2475,6 +2678,16 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" }, + "jsonpath": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", + "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", + "requires": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + } + }, "jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -2499,6 +2712,15 @@ "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=" }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2657,6 +2879,19 @@ "wrappy": "1" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "ordered-read-streams": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", @@ -2675,6 +2910,11 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2786,6 +3026,12 @@ "object-inspect": "^1.9.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, "split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -2806,6 +3052,14 @@ } } }, + "static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "requires": { + "escodegen": "^1.8.1" + } + }, "stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", @@ -2884,6 +3138,14 @@ "is-negated-glob": "^1.0.0" } }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -2905,6 +3167,11 @@ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" }, + "underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, "unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", @@ -2966,6 +3233,11 @@ "is-typed-array": "^1.1.3" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 417fda7..b9a5a4b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homebridge-mqttthing", - "version": "1.1.31", + "version": "1.1.32", "description": "Homebridge plugin supporting various services over MQTT", "main": "index.js", "scripts": { @@ -8,8 +8,8 @@ "start": "homebridge -P . -U test" }, "engines": { - "node": ">4.0.0", - "homebridge": ">=0.2.0" + "homebridge": "^1.3.5", + "node": "^14.18.0 || ^16.10.0" }, "repository": { "type": "git", @@ -69,6 +69,7 @@ "dependencies": { "fakegato-history": "^0.6.2", "homebridge-lib": "~5.1.4", + "jsonpath": "^1.1.1", "mqtt": "^4.2.6" } } diff --git a/test2/backups/instance-backups/homebridge-backup-CA7CA334AA6B.1622945645237.tar.gz b/test2/backups/instance-backups/homebridge-backup-CA7CA334AA6B.1622945645237.tar.gz deleted file mode 100644 index c300ecf..0000000 Binary files a/test2/backups/instance-backups/homebridge-backup-CA7CA334AA6B.1622945645237.tar.gz and /dev/null differ diff --git a/test2/config.json b/test2/config.json index 2a32a4a..bd9060f 100644 --- a/test2/config.json +++ b/test2/config.json @@ -152,11 +152,10 @@ "logMqtt": true }, { - "url": "homebridge", + "url": "homebridge2", "type": "switch", "name": "Tasmota05s", - "username": "admin", - "password": "admin", + "logMqtt": true, "topics": { "getOnline": "tele/tasmota05s/STATE", "getOn": { @@ -361,6 +360,22 @@ } } }, + { + "accessory": "mqttthing", + "type": "statelessProgrammableSwitch", + "name": "Multi-switch", + "url": "homebridge2", + "logMqtt": true, + "topics": { + "getSwitch": [ { + "topic": "test/multiswitch", + "apply": "let msg = JSON.parse( message ); if( msg.id === 1 ) { return msg.state; } return null;" + }, { + "topic": "test/multiswitch", + "apply": "let msg = JSON.parse( message ); if( msg.id === 2 ) { return msg.state; }" + } ] + } + }, { "type": "lightbulb-RGBWW", "name": "Test RGBWW Light",