homebridge-mqttthing
homebridge-mqttthing copied to clipboard
Tampered and Low Battery Status not showing in the Home app
I have 4 sensors in my house that are RF. I'm using an antenna plugged into USB on a RPi4 using a program to convert the signal to MQTT. That gets broadcast with Mosquitto out of the Pi to send the message to MQTT-Thing.
The Sensors345 program is set up so when the status is ok, it returns the message 'OK'. If the battery is low for that topic, it reads 'LOW'. For tampered, it reads 'TAMPER'.
This is what it looks like in the advanced configuration right now (#-ed out numbers for security)
"type": "contactSensor",
"name": "Front Door Sensor",
"url": "mqtt://hidden:1883",
"username": "hidden",
"password": "hidden",
"topics": {
"getContactSensorState": "security/sensors345/sensor/#####/loop1",
"getStatusTampered": {
"topic": "security/sensors345/sensor/#####/tamper",
"apply": "if ( message == 'TAMPER' ) return 1; else return 0;"
},
"getStatusLowBattery": {
"topic": "security/sensors345/sensor/#####/battery",
"apply": "if ( message == 'LOW' ) return 1; else return 0;"
}
},
"integerValue": false,
"onValue": "OPEN",
"offValue": "CLOSED",
"accessory": "mqttthing",
"history": true`
I tried to test this 'if' statement by switching 'TAMPER' and 'LOW' to 'OK', since that's what the MQTT message is showing. In any case, when I open a door, the message is coming through loud and clear. The icon in the Home app shows the door as being open, but two things:
- The 'Status Tampered' shows 'No', even when the message is 'OK' and I'm running the test - meaning I can't get it to say 'Yes' even debugging
- There's no indication at all in the Home app about low battery (the battery is fine - just debugging for the future)
Am I missing something or is this just a limitation between MQTT-Thing and the Home app?
Thanks in advance!
I think the problem might be that all Boolean values need to match your configured onValue and offValue. You might be better using integer values (1 and 0) and putting a corresponding if into the contact sensor state too.
It's definitely something wrong with my 'if' statement. I tried to swap the getContactSensorState with the same type of test and it stopped changing in the Home app.
My input MQTT message is a simple string. What should the 'apply' format be to test the value of the MQTT input string?
Thanks!
Quick update - I think I was able to get this sorted out by doing the following:
"type": "contactSensor",
"name": "Front Door Sensor",
"url": "mqtt://hidden:1883",
"username": "hidden",
"password": "hidden",
"topics": {
"getContactSensorState": {
"topic": "security/sensors345/sensor/#####/loop1",
"apply": "return message == 'OPEN' ? 1 : 0;"
},
"getStatusTampered": {
"topic": "security/sensors345/sensor/#####/tamper",
"apply": "return message == 'OK' ? 1 : 0;"
},
"getStatusLowBattery": {
"topic": "security/sensors345/sensor/#####/battery",
"apply": "return message == 'OK' ? 1 : 0;"
}
},
"integerValue": "true",
"accessory": "mqttthing",
"history": "true"
The changes were:
- setting "integerValue" to "true" (had left on false, but even then, I didn't include the "" around the word 😬 )
- Looked up the ? functionality in JS (I'm not super well-versed) and realized I could return 1 or 0 by just testing if 'message' was exactly equal to a specific thing by writing it out like the above
However, while it's now reflected in the Home app (I set the Low Battery and Tamper test values so 'OK' triggered the 1 command, prompting the notice of either 'yes, tampered' or 'yes, low battery' to be sent to the Home app), I'm seeing this in the logs:
Now, I'm fairly certain that the integer is being returned, but not sure where [true] is getting generated since I'm only passing the integer in my configuration. I'm guessing there's something in the MQTT-Thing script that's converting the integer into either 'true' or 'false' to pass to the Home app, but I still want to make sure that I resolve this error within the HOOBS logs
Thanks again!
I have the same issue... with integerValue
set to true
, I get a validation error but the low battery state seems to be set in homekit. I am unable to clear the low battery state though without a homebridge restart.

config:
{
"accessory": "mqttthing",
"type": "leakSensor",
"name": "Kitchen water leak",
"url": "mqtt://192.168.20.15:1883",
"topics": {
"getLeakDetected": {
"topic": "zigbee2mqtt/waterleak",
"apply": "return JSON.parse(message).water_leak ? 1 : 0;"
},
"getStatusLowBattery": {
"topic": "zigbee2mqtt/waterleak",
"apply": "return JSON.parse(message).battery_low"
},
"getBatteryLevel": {
"topic": "zigbee2mqtt/waterleak",
"apply": "return JSON.parse(message).battery;"
}
},
"integerValue": true,
"logMqtt": true
}
Interestingly, I see the apply() method to be run 4 times, while it is only defined 3 times... If I remove the apply from getStatusLowBattery
, I - correctly - only get 2 apply() log entries.
Played with it a little more... looks like the Home app only polls the low battery state once in a while instead of it being event driven. If I switch away to an other app on my iPhone and immediately back, the right low battery status shows up right away. This update seems to be pushed to other devices as well. As soon as one device gets the status right, all of them get it right.
@andrasg : I have the same issues here. I can only get it to work when I set integerValue to true. Despite it is mentioned differently in the documentation I get the same warning too:
My config:
"getStatusLowBattery": {
"topic": "openWB/lp/1/%Soc",
"apply": "if (JSON.parse(message) < 20) return 1; else return 0;"
},
leads to:
[3/5/2022, 4:29:28 PM] [EQA] Ignoring invalid value [true] for Status Low Battery - not an integer
[3/5/2022, 4:29:28 PM] [EQA] apply() function decoded message to [true]
It's not a problem for now as HomeKit works as expected :smile: . The way of clearing the low battery event is a little bit annoying but the more Apple devices one uses the higher is the chance to see the correct state.
same here with getStatusFault
{
"accessory": "mqttthing",
"type": "contactSensor",
"name": "Porte Soggiorno - Cucina - Camera Armadi",
"url": "mqtt://127.0.0.1:1883",
"username": "XXXX",
"password": "XXXX",
"caption": "Zona 38: Porte Soggiorno - Cucina - Camera Armadi",
"topics": {
"getContactSensorState": {
"topic": "ialarm/sensors/zone_38",
"apply": "if (JSON.parse(message).status > 1) return 1; else return 0;"
},
"getStatusActive": {
"topic": "ialarm/sensors/zone_38",
"apply": "return JSON.parse(message).ok;"
},
"getStatusFault": {
"topic": "ialarm/sensors/zone_38",
"apply": "return JSON.parse(message).fault;"
},
"getStatusLowBattery": {
"topic": "ialarm/sensors/zone_38",
"apply": "return JSON.parse(message).lowbat;"
}
},
"integerValue": true
}
log console homebridge:
[3/6/2022, 10:06:39 AM] [Porte Soggiorno - Cucina - Camera Armadi] Ignoring invalid value [true] for Status Fault - not an integer
[3/6/2022, 10:06:56 AM] [Porte Soggiorno - Cucina - Camera Armadi] Ignoring invalid value [false] for Status Fault - not an integer
I did a quick look at the plugin's source code. The log message is thrown in function IsValid()
function isValid( charac, value ) {
// if validation is disabled, accept anything
if( config.validate === false ) {
return true;
}
const format = charac.props.format;
if( format === 'int' || format === "uint8" || format == "uint16" || format == "uint32" ) {
if( ! Number.isInteger( value ) ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - not an integer` );
return false;
}
if( isSet( charac.props.minValue ) && value < charac.props.minValue ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - below minimum (${charac.props.minValue})` );
return false;
}
if( isSet( charac.props.maxValue ) && value > charac.props.maxValue ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - above maximum (${charac.props.maxValue})` );
return false;
}
} else if( format === 'float' ) {
if( typeof value !== 'number' || isNaN( value ) ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - not a number` );
return false;
}
if( isSet( charac.props.minValue ) && value < charac.props.minValue ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - below minimum (${charac.props.minValue})` );
return false;
}
if( isSet( charac.props.maxValue ) && value > charac.props.maxValue ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - above maximum (${charac.props.maxValue})` );
return false;
}
} else if( format === 'bool' ) {
if( value !== true && value !== false ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - not a Boolean` );
return false;
}
} else if( format === 'string' ) {
if( typeof value !== 'string' ) {
log( `Ignoring invalid value [${value}] for ${charac.displayName} - not a string` );
return false;
}
} else {
log( `Unable to validate ${charac.displayName}, format [${charac.props.format}] - ${JSON.stringify(charac)}` );
}
return true;
}
The StatusLowBattery characteristic is of type Boolean. It is also created as such in the code. However, the format variable contains "uint8". That's why the message is logged. I have not found the location in the code where it is modified...