openhab / openhab-addons

Add-ons for openHAB
https://www.openhab.org/
Eclipse Public License 2.0
1.88k stars 3.59k forks source link

[mqtt] MQTT thing stops working after receiving "null" message #15282

Closed stueble closed 1 year ago

stueble commented 1 year ago

MQTT things stop receiving values after a null message has been send by the broker. Other clients, such as mosquitto_sub, continue working, therefore it seems to be a bug of the openhab MQTT plugin. I found several similar topics discussed by the openhab community, but none of them discussed a solution.

I tried to provide any information I have. If you need more, or have an idea how I can increase debugging output, I am happy to support.

Expected Behavior

After receiving the (truncated, invalid) message, the MQTT thing should continue receiving valid values, as other clients such as mqtt-explorer or mosquitto_sub do.

Current Behavior

Took a while to break down the problem:

I have a generic MQTT thing that expects to receive a Number in JSON format, thus it also includes a transformation pattern: JSONPATH:$.value

mosquitto_sub connecting to the same (mosquitto) broker receives outputs the following data:

Client (null) received PUBLISH (d0, q0, r0, m0, 'N/xxxxxxxx/grid/0/Ac/Power', ... (16 bytes))
2023-07-21T13:22:34+0200 N/xxxxxxxxxxxx/grid/0/Ac/Power 7b2276616c7565223a202d343331357d
Client (null) received PUBLISH (d0, q0, r0, m0, 'N/xxxxxxxx/grid/0/Ac/Power', ... (16 bytes))
2023-07-21T13:22:36+0200 N/xxxxxxxxxxxx/grid/0/Ac/Power 7b2276616c7565223a202d343334397d
Client (null) received PUBLISH (d0, q0, r0, m0, 'N/xxxxxxxx/grid/0/Ac/Power', ... (0 bytes))
2023-07-21T13:22:38+0200 N/xxxxxxxxxxxx/grid/0/Ac/Power 
Client (null) received PUBLISH (d0, q0, r0, m0, 'N/xxxxxxxx/grid/0/Ac/Power', ... (15 bytes))
2023-07-21T13:22:40+0200 N/xxxxxxxxxxxx/grid/0/Ac/Power 7b2276616c7565223a206e756c6c7d
Client (null) received PUBLISH (d0, q0, r0, m0, 'N/xxxxxxxx/grid/0/Ac/Power', ... (16 bytes))
2023-07-21T13:22:42+0200 N/xxxxxxxxxxxx/grid/0/Ac/Power 7b2276616c7565223a202d343334317d
Client (null) received PUBLISH (d0, q0, r0, m0, 'N/xxxxxxxx/grid/0/Ac/Power', ... (16 bytes))
2023-07-21T13:22:44+0200 N/xxxxxxxxxxxx/grid/0/Ac/Power 7b2276616c7565223a202d343332387d
<!-- If you're suggesting a change/improvement, tell us how it should work -->

First, two valid (16 byte) data are received. Then the client sends a 0 bytes balue and a 15 bytes value. After that, it continues to output the valid (16 byte) values. In contrast, openhab only outputs values until the "null" is received. From then on, the item is not updated any more. Unfortunately, I was not able to get any debug log in incoming mqtt messages, although I set all bundles with MQTT in its name to TRACE.

The only debug messages I receive are like this:

2023-07-21 13:21:28.370 [TRACE] [.MqttChannelStateDescriptionProvider] - Providing state description for channel mqtt:topic:venus:Venus_SolarEdge:Grid_Power
2023-07-21 13:21:28.371 [TRACE] [.MqttChannelStateDescriptionProvider] - Providing state description for channel mqtt:topic:venus:Venus_SolarEdge:Grid_Power
2023-07-21 13:21:28.377 [DEBUG] [qtt.generic.AbstractMQTTThingHandler] - Successfully published value -4647 W to topic openWB/set/evu/W

Possible Solution

The thing works again if either the MQTT thing or the MQTT broker is restarted, but it is not a realistic workaround.

Context

The bug occurs in the context of a Victron Battery Storage System connected to openhab. Openhab writes the received values to a connected OpenWB wallbox. Both data logging and wallbox stop working when the thing hangs. The invalid values are send by the Victron system when a PV inverted connected via modbus (to the victron system) is not reachable (which unfortunately resets the connection every night). But this reset is an expected behavior.

As already mentioned above, you find in the openhab community several discussions around this problem.

Your Environment

openhab 3.4.4 (docker) Victron VenusOS on Raspberry Pi

openhab> bundle:list -s | grep mqtt
238 │ Active │  80 │ 1.2.2                  │ com.hivemq.client.mqtt
280 │ Active │  80 │ 3.4.4                  │ org.openhab.binding.mqtt
281 │ Active │  81 │ 3.4.4                  │ org.openhab.binding.mqtt.espmilighthub
282 │ Active │  81 │ 3.4.4                  │ org.openhab.binding.mqtt.generic
283 │ Active │  82 │ 3.4.4                  │ org.openhab.binding.mqtt.homeassistant
284 │ Active │  82 │ 3.4.4                  │ org.openhab.binding.mqtt.homie
300 │ Active │  80 │ 3.4.4                  │ org.openhab.core.io.transport.mqtt
openhab-bot commented 1 year ago

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/oh3-mqtt-binding-not-reacting-to-incoming-payloads/142008/21

J-N-K commented 1 year ago

Do you have a set of commands (e.g. with mosquitto_pub) that can reproduce that behavior and does not depend on some external hardware that I don't own?

stueble commented 1 year ago

That was easier than expected:

I can reproduce the bug when using a thing with incoming transformation "JSONPATH:$.value"

Sending the following message changes the corresponding item mosquitto_pub -d -h mosquittobroker -t "My/Test" -m '{"value": -6500}'

The item does not change any more after sending the following NULL message: mosquitto_pub -d -h mosquittobroker -t "My/Test" -n

As described above, after restarting the thing everything works again. Since I could not reproduce the bug without transformation, my expectation is that the JSONPATH transformation breaks something when the null message is received. Maybe.

J-N-K commented 1 year ago

It would have been easier if someone had mentioned this exception in the log:

java.lang.IllegalArgumentException: json string can not be null or empty
    at com.jayway.jsonpath.internal.Utils.notEmpty(Utils.java:383)
    at com.jayway.jsonpath.internal.ParseContextImpl.parse(ParseContextImpl.java:36)
    at com.jayway.jsonpath.JsonPath.read(JsonPath.java:498)
    at org.openhab.transform.jsonpath.internal.JSonPathTransformationService.transform(JSonPathTransformationService.java:63)
    at org.openhab.binding.mqtt.generic.ChannelStateTransformation.processValue(ChannelStateTransformation.java:91)
    at org.openhab.binding.mqtt.generic.ChannelState.processMessage(ChannelState.java:176)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.processMessage(Subscription.java:88)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.lambda$1(Subscription.java:84)
    at java.base/java.util.concurrent.ConcurrentHashMap$KeySpliterator.forEachRemaining(ConcurrentHashMap.java:3573)
    at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.messageArrived(Subscription.java:84)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.messageArrived(Subscription.java:69)
    at com.hivemq.client.internal.mqtt.mqtt3.Mqtt3AsyncClientView.lambda$callbackView$1(Mqtt3AsyncClientView.java:76)
    at com.hivemq.client.internal.mqtt.MqttAsyncClient$CallbackSubscriber.onNext(MqttAsyncClient.java:303)
    at com.hivemq.client.internal.mqtt.MqttAsyncClient$CallbackSubscriber.onNext(MqttAsyncClient.java:288)
    at com.hivemq.client.rx.FlowableWithSingle$SingleFutureSubscriber.onNext(FlowableWithSingle.java:406)
    at com.hivemq.client.internal.rx.operators.FlowableWithSingleCombine$SplitSubscriber$Default.tryOnNextActual(FlowableWithSingleCombine.java:235)
    at com.hivemq.client.internal.rx.operators.FlowableWithSingleCombine$SplitSubscriber.tryOnNext(FlowableWithSingleCombine.java:200)
    at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnConditionalSubscriber.runAsync(FlowableObserveOn.java:649)
    at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
    at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
    at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Exception in thread "RxComputationThreadPool-6" java.lang.IllegalArgumentException: json string can not be null or empty
    at com.jayway.jsonpath.internal.Utils.notEmpty(Utils.java:383)
    at com.jayway.jsonpath.internal.ParseContextImpl.parse(ParseContextImpl.java:36)
    at com.jayway.jsonpath.JsonPath.read(JsonPath.java:498)
    at org.openhab.transform.jsonpath.internal.JSonPathTransformationService.transform(JSonPathTransformationService.java:63)
    at org.openhab.binding.mqtt.generic.ChannelStateTransformation.processValue(ChannelStateTransformation.java:91)
    at org.openhab.binding.mqtt.generic.ChannelState.processMessage(ChannelState.java:176)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.processMessage(Subscription.java:88)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.lambda$1(Subscription.java:84)
    at java.base/java.util.concurrent.ConcurrentHashMap$KeySpliterator.forEachRemaining(ConcurrentHashMap.java:3573)
    at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.messageArrived(Subscription.java:84)
    at org.openhab.core.io.transport.mqtt.internal.Subscription.messageArrived(Subscription.java:69)
    at com.hivemq.client.internal.mqtt.mqtt3.Mqtt3AsyncClientView.lambda$callbackView$1(Mqtt3AsyncClientView.java:76)
    at com.hivemq.client.internal.mqtt.MqttAsyncClient$CallbackSubscriber.onNext(MqttAsyncClient.java:303)
    at com.hivemq.client.internal.mqtt.MqttAsyncClient$CallbackSubscriber.onNext(MqttAsyncClient.java:288)
    at com.hivemq.client.rx.FlowableWithSingle$SingleFutureSubscriber.onNext(FlowableWithSingle.java:406)
    at com.hivemq.client.internal.rx.operators.FlowableWithSingleCombine$SplitSubscriber$Default.tryOnNextActual(FlowableWithSingleCombine.java:235)
    at com.hivemq.client.internal.rx.operators.FlowableWithSingleCombine$SplitSubscriber.tryOnNext(FlowableWithSingleCombine.java:200)
    at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnConditionalSubscriber.runAsync(FlowableObserveOn.java:649)
    at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
    at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
    at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
stueble commented 1 year ago

I don't have this exception. At least not in openhab.log

stueble commented 1 year ago

Many thanks. For those who have the same problem, a possible workaround is to replace the JSONPATH transformation by a Javascript transformation, e.g.,

(function(i) {
    if (i == "") {
        return null;
    }
    var data = JSON.parse(i);
    return data.value;
})(input)

Please note that the above function is not 100% correct, as it seems to be impossible to send an "UNDEF" to a NumberValue in a Javascript transformation.