From 027dccb533e7a91932fcaff496edf1aeb1faddd0 Mon Sep 17 00:00:00 2001 From: David Miller Date: Sat, 14 Mar 2020 14:15:33 +0000 Subject: [PATCH] Handle bare JavaScript Booleans returned from apply() functions, and pass values for Boolean properties to apply functions without prior conversion to string --- README.md | 13 ++++++++ index.js | 86 ++++++++++++++++++++++++++++++++++++------------ package.json | 2 +- test/config.json | 2 +- 4 files changed, 80 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 1b09e5f..25b7318 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,19 @@ # homebridge-mqttthing Homebridge plugin supporting various services over MQTT, originally based on homebrige-mqttswitch and homebridge-mqttlightbulb +--- + * [Installation](#installation) * [Configuration](#configuration) * [Supported Accessories](#supported-accessories) * [Release notes](#release-notes) +## Compatibility with previous versions + +**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](#release-notes) section. + # Installation Follow the instructions in [homebridge](https://www.npmjs.com/package/homebridge) for the homebridge server installation. This plugin is published through [NPM](https://www.npmjs.com/package/homebridge-mqttthing) and should be installed "globally" by typing: @@ -1277,6 +1285,11 @@ Window covering position state can be **DECREASING**, **INCREASING** or **STOPPE # Release notes +Version 1.1.1 ++ Changed Boolean value handling to support bare JavaScript Booleans returned from incoming MQTT apply() functions (`"true" != true` but both are now accepted). ++ Boolean property values passed to outgoing MQTT apply() functions are no-longer converted to strings first (for consistency with the change above). This allows easier publishing of JSON containing correctly typed values, but **may change outgoing message format with existing configurations in some situations**. ++ Added option to configure garage door target values independently of current values - thanks, Charles Powell + Version 1.0.50 + Stateless Programmable Switch: allow multiple buttons under a single switch - thanks, Jacob Nite diff --git a/index.js b/index.js index 702e740..9cae3e0 100644 --- a/index.js +++ b/index.js @@ -247,9 +247,10 @@ function makeThing(log, config) { }; } - // Convert from boolean true/false to MQTT-published boolean value (as configured) - returns null if no offValue - function onOffValue(value) { - var mqttval; + //! Determine appropriate on/off value for Boolean property (not forced to string) for MQTT publishing. + //! Returns null if no offValue. + function getOnOffPubValue( value ) { + let mqttval; if( config.onValue ) { // using onValue/offValue mqttval = value ? config.onValue : config.offValue; @@ -261,18 +262,59 @@ function makeThing(log, config) { if( mqttval === undefined || mqttval === null ) { return null; } else { - return mqttval.toString(); + return mqttval; } } - function onlineOfflineValue( value ) { + //! Test whether a value represents 'on' + function isRecvValueOn( mqttval ) { + let onval = getOnOffPubValue( true ); + return mqttval === onval || mqttval == ( onval + '' ); + } + + //! Test whether a value represents 'off' + function isRecvValueOff( mqttval ) { + let offval = getOnOffPubValue( false ); + + if( offval === null ) { + // there is no off value + return false; + } + + if( mqttval === offval || mqttval == ( offval + '' ) ) { + // off value match - it's definitely off + return true; + } + + if( config.otherValueOff ) { + if( ! isRecvValueOn( mqttval ) ) { + // it's not the on value and we consider any other value to be off + return true; + } + } + + // not off + return false; + } + + function getOnlineOfflinePubValue( value ) { var pubVal = ( value ? config.onlineValue : config.offlineValue ); if( pubVal === undefined ) { - pubVal = onOffValue( value ); + pubVal = getOnOffPubValue( value ); } return pubVal; } + function isRecvValueOnline( mqttval ) { + let onval = getOnlineOfflinePubValue( true ); + return mqttval === onval || mqttval == ( onval + '' ); + } + + function isRecvValueOffline( mqttval ) { + let offval = getOnlineOfflinePubValue( false ); + return mqttval === offval || mqttval == ( offval + '' ); + } + function mapValueForHomebridge(val, mapValueFunc) { if (mapValueFunc) { return mapValueFunc(val); @@ -313,7 +355,7 @@ function makeThing(log, config) { charac.on('set', function (value, callback, context) { if (context !== c_mySetContext) { state[property] = value; - publish( onOffValue( value ) ); + publish( getOnOffPubValue( value ) ); } callback(); @@ -326,7 +368,7 @@ function makeThing(log, config) { autoOffTimer = null; state[property] = false; - publish( onOffValue( false ) ); + publish( getOnOffPubValue( false ) ); service.getCharacteristic(characteristic).setValue(mapValueForHomebridge(false, mapValueFunc), undefined, c_mySetContext); }, turnOffAfterms ); @@ -340,16 +382,15 @@ function makeThing(log, config) { // subscribe to get topic if (getTopic) { mqttSubscribe(getTopic, function (topic, message) { + // determine whether this is an on or off value let newState = false; // assume off - if( message == onOffValue( true ) ) { + if( isRecvValueOn( message ) ) { newState = true; // received on value so on - } else { - let offValue = onOffValue( false ); - if( offValue !== null && message != offValue && ! config.otherValueOff ) { - // there is a specific off value, but we've received something else - so ignore message - return; - } + } else if ( ! isRecvValueOff( message ) ) { + // received value NOT acceptable as 'off' so ignore message + return; } + // if it changed, set characteristic if (state[property] != newState) { state[property] = newState; service.getCharacteristic(characteristic).setValue(mapValueForHomebridge(newState, mapValueFunc), undefined, c_mySetContext); @@ -369,21 +410,24 @@ function makeThing(log, config) { } } - function booleanState( property, getTopic, initialValue, onOffFunc ) { + function booleanState( property, getTopic, initialValue, isOnFunc, isOffFunc ) { // default state state[ property ] = ( initialValue ? true : false ); // MQTT subscription if( getTopic ) { mqttSubscribe( getTopic, function( topic, message ) { - var newState = ( message == onOffFunc( true ) ); - state[ property ] = newState; + if( isOnFunc( message ) ) { + state[ property ] = true; + } else if( isOffFunc( message ) ) { + state[ property ] = false; + } } ); } } function state_Online() { - booleanState( 'online', config.topics.getOnline, true, onlineOfflineValue ); + booleanState( 'online', config.topics.getOnline, true, isRecvValueOnline, isRecvValueOffline ); } function integerCharacteristic(service, property, characteristic, setTopic, getTopic, initialValue) { @@ -1966,7 +2010,7 @@ function makeThing(log, config) { function timerFunc() { durationTimer = null; state.active = false; - mqttPublish( config.topics.setActive, onOffValue( false ) ); + mqttPublish( config.topics.setActive, getOnOffPubValue( false ) ); characActive.updateValue( Characteristic.Active.INACTIVE ); } @@ -1986,7 +2030,7 @@ function makeThing(log, config) { charac.updateValue( getRemainingDuration() ); }); } else { - // device will handle the timer by itfelf + // device will handle the timer by itself characActive.on('change', function (obj) { if ( obj.newValue == Characteristic.Active.ACTIVE ) { state.durationEndTime = Math.floor(Date.now() / 1000) + state.setDuration; diff --git a/package.json b/package.json index f94f7ce..9d3d1ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homebridge-mqttthing", - "version": "1.0.50", + "version": "1.1.1", "description": "Homebridge plugin supporting various services over MQTT", "main": "index.js", "scripts": { diff --git a/test/config.json b/test/config.json index 8e96db0..6a27e67 100644 --- a/test/config.json +++ b/test/config.json @@ -86,7 +86,7 @@ "setOn": { "topic": "test/rgblight/on/set", "apply": "return JSON.stringify( { state: { power: message }, version: 1 } );" } }, "logMqtt": true, - "integerValue": true, + "integerValue": false, "hex": true }, {