Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduces standardized key for govee binary moisture sensors #2625

Merged
merged 1 commit into from
Sep 27, 2023

Conversation

kcpants
Copy link
Contributor

@kcpants kcpants commented Sep 19, 2023

This pull request aims to introduce a new standard json key, wet, to represent whether a binary moisture sensor detects water or not and applies the new key to the H5054 Govee Water Leak Detector device. This key will be added to the mappings dictionary in rtl_433_mqtt_hass.py in a separate pull request so that devices which publish MQTT messages with this key will be automatically discovered as binary moisture sensor devices in HA.

Notes

  • Based on the discussion in pull request Add support for Govee water sensors to rtl_433_mqtt_hass #2605, wet or moist seemed like the most appropriate keys to represent binary moisture sensors. I chose wet but would be happy to change the key name if requested.
  • Ideally, the new key could replace the event key in this decoder. However, existing deployments almost certainly rely on it so it seems safest to leave it in place so as not to disrupt anyone's home ecosystem.
  • This decoder file actually contains two decoders, the first of which simultaneously applies to both older govee water sensors and Govee B5023 door contact sensors. The new key should be added to both decoders but I do not own any of these older sensors so would be unable to test changes made to the older decoder. I'd like to avoid pushing untested code so have limited my changes to the new decoder.
  • These govee sensors are somewhat odd in that they don't send a message when they transition from wet to dry (only dry to wet and periodically while wet). As such, the "button pressed" signal is used as a proxy for the wet-to-dry transition. The assumption being that a button press implies that a human has investigated and fixed (or is at least aware of) the underlying leak. This is in-line with how the HA community manually configures these sensors (see this HA community thread for a more in-depth discussion).

@zuckschwerdt
Copy link
Collaborator

I like the logic. The key name of "wet" seems somewhat short (almost like an acronym). Is there any grouping prefix part we could add to generalize this to other similar sensors? Something like state, detect, trigger, report, signal, … As in report_wet

@kcpants kcpants force-pushed the standardize_govee_keys branch from a9bc818 to 403abe9 Compare September 19, 2023 16:30
@kcpants
Copy link
Contributor Author

kcpants commented Sep 19, 2023

Sure! Scanning through the existing device mappings in rtl_433_mqtt_hass.py, it looks like the other two binary sensors do not use a common prefix so I don't think there is any established convention yet and we are free to pick any prefix without creating inconsistencies.

From rtl_433_mqtt_hass.py the json keys tamper and alarm are used to indicate binary sensors with the device_class "safety":

    "tamper": {
        "device_type": "binary_sensor",
        "object_suffix": "tamper",
        "config": {
            "device_class": "safety",
            "force_update": "true",
            "payload_on": "1",
            "payload_off": "0",
            "entity_category": "diagnostic"
        }
    },

    "alarm": {
        "device_type": "binary_sensor",
        "object_suffix": "alarm",
        "config": {
            "device_class": "safety",
            "force_update": "true",
            "payload_on": "1",
            "payload_off": "0",
            "entity_category": "diagnostic"
        }
    }

The Home Assistant UI conveys the state of binary moisture sensors as "wet" or "dry" so I do think "wet" should be part of the key. Of the prefixes you suggested, I like detect_wet with report_wet and state_wet as my second and third choices. "detect_" or "report_" seems most applicable to other binary sensors supported by HA (e.g. "detect_vibration", "detect_gas", "detect_sound", "detect_smoke", "detect_presence") whereas state doesn't seems to fit as well with all of them ("state_gas", "state_sound"). I've changed the key to detect_wet and included a screenshot of the binary sensors supported by home assistant below for reference:

binary_sensor_classes_icons

@kcpants
Copy link
Contributor Author

kcpants commented Sep 20, 2023

For the sake of thoroughness, here is some sample output from MQTT Explorer after the latest changes:

Button Press (detect_wet = 0)

{
  "time": "2023-09-20T13:21:15.708465-0400",
  "model": "Govee-Water",
  "id": 21723,
  "event": "Button Press",
  "detect_wet": 0,
  "code": "54db30543b5b",
  "mic": "CRC"
}

Manually Shorted Sensor Contacts (detect_wet = 1)

{
  "time": "2023-09-20T13:23:43.165739-0400",
  "model": "Govee-Water",
  "id": 21723,
  "event": "Water Leak",
  "detect_wet": 1,
  "leak_num": 9,
  "code": "54db3209d661",
  "mic": "CRC"
}

Let me know if you'd like me to make any more changes. Looking forward to getting this merged!

@gdt
Copy link
Collaborator

gdt commented Sep 22, 2023

I think Wet is the human-facing description and the name of the sensor is "moisture" with values ON/OFF or whatever the standard rtl_433 yes/no values are.

We need to be careful about defining semantics for moisture sensors in general vs what I see as a slightly odd case. I think you are doing that. However, rtl_433 tends to be "just convert the on-air bits to json", and this is heading down the path of representing semantics. Obviously we need -- someplace, really not clear where -- some kind of stateful processing. This is clearly before.

But, as a way to detangle the confused representation into "this report says wet" and "this report says not wet", this seems like progress.

We also need to ask: does every message contain a wet/not-wet message, or are they only sometimes present? If sometimes, is it the case that sometimes this field will be present, and sometimes not? I think it's important not to report moisure:false when the over-the-air bits did not claim that.

@gdt
Copy link
Collaborator

gdt commented Sep 22, 2023

Good point about moisture being something with units, but I see that as being in a sensor.foo, while in a binary_sensor.foo it is obviously true/false. So I think I'm still ok with moisture. I'm also ok with wet.

@kcpants
Copy link
Contributor Author

kcpants commented Sep 22, 2023

I don't have that much experience with Home Assistant or this codebase so I'm happy to defer to whatever key name / prefix is preferred by the maintainers. As I understand it, the goal is for the new key to be general enough that the decoders for any well-behaved binary moisture sensors can directly convert their raw wet/dry signals to it. I originally chose "wet" because it seemed like the most concise way to describe the state of an arbitrary binary moisture sensor (a sensor that detects the presence of moisture is either "wet" or "dry"). However, this naming convention doesn't apply cleanly to other binary sensors (e.g. there is no obvious adjective to describe when a sound sensor has heard a sound or a vibration sensor has felt a vibration etc) so I see the benefit of introducing a standard prefix like "detect" or "report" for all binary sensors so that they can all have a common naming scheme (i.e. "detect_<noun the binary sensor detects>").

We need to be careful about defining semantics for moisture sensors in general vs what I see as a slightly odd case. I think you are doing that. However, rtl_433 tends to be "just convert the on-air bits to json", and this is heading down the path of representing semantics.

I definitely understand the desire to keep higher-level semantics out of the rtl_433 codebase. However, I think the fact that the other PR transformed into a support ticket is evidence that the current decoder implementation makes these sensors difficult enough to integrate with HA today that they cause frustration and waste folks' time so some change is warranted to make them work more seamlessly out-of-the-box.

As discussed in the other PR, these sensors are quirky but in the end they are best represented as binary moisture sensors in HA. Given that automatic device discovery in rtl_433_mqtt_hass.py is performed on the json data output by the decoder, to me it makes sense to deal with their strangeness within their govee-specific decoder, where the device's raw signal bytes are transformed into json text, rather than letting their strangeness "seep-out" into rtl_433_mqtt_hass.py.

We also need to ask: does every message contain a wet/not-wet message, or are they only sometimes present?

In addition to wet/not-wet, this sensor sends occasional battery health messages. These messages are converted into json without the new detect_wet key. Omitting detect_wet from the battery messages is handled by the following code in combination with the DATA_COND line ("detect_wet", "", DATA_COND, wet >= 0, DATA_INT, wet,) in the call to data_make:

    char const *event_str;
    int wet = -1;
    int leak_num = -1;
    int battery  = -1;
    switch (event) {
    case 0x0:
        event_str = "Button Press";
        wet = 0;
        break;
    case 0x1:
        event_str = "Battery Report";
        battery   = event_data;
        break;
    case 0x2:
        event_str = "Water Leak";
        wet = 1;
        leak_num  = event_data;
        break;

@zuckschwerdt
Copy link
Collaborator

zuckschwerdt commented Sep 23, 2023

I'm good with this addition, also as template for the general case. It's a sensor to detect conditions. It detect_wet's a liquid condition. It could be report_liquid and I don't really prefer one or the other.
Let's wait a few days for others to get a word in, then merge and make this style a recommendation.

@gdt
Copy link
Collaborator

gdt commented Sep 23, 2023

I like detect_wet. To me the important aspect of a name is the property that most people who see it without reading the definition will jump to the right conclusion. (I don't care what the variables are in the C code really, but it's nice if they match, to avoid confusing readers.) So I'm +1 on merging in concept, and +1 specifically on detect_wet.

I don't like report_wet as that sounds like a notification of a change, rather than a binary sensor. Yes, that's what Govee does sort of, but the generic word in rtl_433 that aims to align with HA's binary sensor concept (wihether used in HA or elsewhere) should have a "this is a state yes or no and the value is now X" flavor. (Sorry to ramble so much but these decisions live forever and can consume a lot of downstream brain time by many readers over the long-term future.)

@zuckschwerdt
Copy link
Collaborator

My sentiment exactly, we can only decide this once and then users will depend on it staying that way. Thanks for the feedback on detect_, I don't see event_ here because it didn't just happen and state_ does not convey any information. Wasn't sure on report_ though.

@Vlad-Star
Copy link

detect_wet makes better sense to me as well.

One question regarding
These govee sensors are somewhat odd in that they don't send a message when they transition from wet to dry (only dry to wet and periodically while wet). As such, the "button pressed" signal is used as a proxy for the wet-to-dry transition

Would it make sense to implement a timer? I.e. if condition is no longer reported "wet" for NN seconds - we assume it to be "dry".

@kcpants
Copy link
Contributor Author

kcpants commented Sep 24, 2023

we can only decide this once and then users will depend on it staying that way

I completely understand waiting a few days for feedback. Since the json is exposed via MQTT it's essentially a read-only API. Once committed, other software will begin to depend on it at which point it will be impossible to change without breaking things for people downstream.

@gdt
Copy link
Collaborator

gdt commented Sep 24, 2023

detect_wet makes better sense to me as well.

One question regarding These govee sensors are somewhat odd in that they don't send a message when they transition from wet to dry (only dry to wet and periodically while wet). As such, the "button pressed" signal is used as a proxy for the wet-to-dry transition

Would it make sense to implement a timer? I.e. if condition is no longer reported "wet" for NN seconds - we assume it to be "dry".

This is contrary to doctrine. See #2635

@kcpants
Copy link
Contributor Author

kcpants commented Sep 24, 2023

Would it make sense to implement a timer? I.e. if condition is no longer reported "wet" for NN seconds - we assume it to be "dry".

Given my (admittedly limited) understanding, I would be hesitant to implement a timer within the decoder for the following reasons:

  1. It seems like the decoders are designed to simply convert radio signals to json. Maintaining state within a decoder feels like it would go against the philosophy of rtl_433.
  2. Furthermore, introducing state adds complexity that may not be obvious at first. For example, suppose the timer is implemented to send a dry message if the last message was wet and no wet message has been received in the last NN seconds as suggested. This requires storing the time since the last wet signal was received in the memory of the rtl_433 process. If rtl_433 were restarted this state would be lost and no dry signal would ever be sent.
  3. Adding a timer would introduce the possibility of false negatives. For example, suppose a leak sensor is being used beneath an appliance (e.g. dishwasher or laundry machine) which is resting on ground that has some natural slope or way for water to flow away from the appliance. The appliance might leak while it's operating, trigger the sensor, then the liquid may drain within the first timer interval which would switch the sensor to dry. It seems better for the state of the sensor to remain unchanged as "wet" until a human has investigated.
  4. It's not obvious what the ideal timer length should be. It would clearly need to be at least as long as the interval between wet signals but should there be any padding to account for missed signals? This would be especially relevant to people who are using a single SDR receiver for multiple frequencies with rtl_433 cycling between them. In such a configuration, missed signals may be expected.

Overall, even if implementing a timer at this layer were allowed, it's not clear that the solution would be correct for all users. As such I think it's something that should be implemented in HA on a per-user basis, if desired.

@rct
Copy link
Contributor

rct commented Sep 24, 2023

I bought a bunch of Govee water sernsors a while back because they are cheap and a number of bloggers seemed to be using them without going deep enough into the details. I'm not doing much with them other than using them as an audible warning.

Correct me if I'm wrong, but no amount of coding/heuristics is going to fix the problem that the only thing you actually know is that you might eventually get some sort of event message from the Govee sensors. Then again you may never receive another message. It does not send states such as dry, or even reliable heartbeat/battery message that lets you know it is still there and still able to be received.

Trying to synthesize device state from these optimistic event messages is likely to be wrong in some case and give a false sense of security. If you don't get any more messages from the sensor -- you don't know that it is actually dry or actually still working and capable of being received. So, don't obscure that fact to the user, it could lead to real damage.

  • These govee sensors are somewhat odd in that they don't send a message when they transition from wet to dry (only dry to wet and periodically while wet). As such, the "button pressed" signal is used as a proxy for the wet-to-dry transition. The assumption being that a button press implies that a human has investigated and fixed (or is at least aware of) the underlying leak. This is in-line with how the HA community manually configures these sensors (see this HA community thread for a more in-depth discussion).

Since the device itself can't be trusted, the requirement that the device be visited seems about the only reasonable thing to do. Though after pushing the button, do you actually know that the device is in the dry state and will re-trigger when it gets wet again?

@Vlad-Star
Copy link

Thank you @gdt and @kcpants for the detailed explanations, I appreciate it!

So, where do you think would be the right place to add such a sensor model-dependent workarounds (quirks) that would "standardize" their behavior? (i.e. somewhat similar to ZHA quirks)

@rct
Copy link
Contributor

rct commented Sep 24, 2023

So, where do you think would be the right place to add such a sensor model-dependent workarounds (quirks) that would "standardize" their behavior? (i.e. somewhat similar to ZHA quirks)

If you wanted to follow the Zigbee model, something closer to Zigbee2MQTT -- use something like the mqtt relay script to process rtl_433 output and then send it out over MQTT for Home Assistant to consume.

 rtl_433 - (syslog/json, mqtt, pipe, etc.) --> rtl_4332MQTT processor --> MQTT --> Home Assistant

@kcpants
Copy link
Contributor Author

kcpants commented Sep 25, 2023

Though after pushing the button, do you actually know that the device is in the dry state and will re-trigger when it gets wet again?

I just performed a few manual tests on my device and I think the sensor operates more like a simple switch. On the model I have, when the metal contacts are shorted by liquid a circuit is closed, the alarm triggers, and a "wet" message is transmitted. As long as the circuit remains closed the "wet" message is continuously transmitted each time the audible alarm fires (one message every 2-3 seconds). When the liquid is wiped off the alarm stops and no more messages are transmitted. If the contacts are shorted again a new "wet" message is sent (regardless of whether the button is pressed beforehand or not) and, on newer models, the "leak number" in the message is incremented by one. I don't believe anything needs to be done to "re-arm" the sensor since it operates by blindly transmitting wet messages when the circuit is closed.

Given this behavior, implementing a timer in a relay script might work for the common case but would likely report incorrectly in some scenarios (e.g. if the battery failed while wet or the internal transmitter or local receiver failed). Using the button press signal to represent "dry" in Home Assistant seems much safer since it implies a human investigated and pushed the button and aligns with what the HA community has converged on.

Correct me if I'm wrong, but no amount of coding/heuristics is going to fix the problem that the only thing you actually know is that you might eventually get some sort of event message from the Govee sensors. Then again you may never receive another message. It does not send states such as dry, or even reliable heartbeat/battery message that lets you know it is still there and still able to be received.

As described above, I would say these sensors reliably transmit messages to represent the "wet state" when shorted but do not send anything when they are dried off. The "event" json key in the decoder is overloaded and arguably misused, likely because the original govee decoder covers two different govee sensors (leak and door) but maps data to a single json key.

The device is best represented as a binary moisture sensor in HA but lacks any message which indicates that they are in the dry state (from their perspective, they are dry if they are not transmitting "wet") meaning the HA entity would never transition back to dry once wet. Since adding any timing / state would almost certainly do more harm than good, the "button pressed" message is used to transition the binary moisture sensor entity in Home Assistant from wet to dry (it has no state-changing effect on the device itself).

Additionally, based on the code, the sensor also occasionally sends battery health messages. I can confirm that they do send a battery message when the battery is first inserted, beyond that I'm not sure. I hope that when the battery runs low at minimum they start to beep (like a smoke detector) but I've not owned mine long enough to observe that behavior.

@Vlad-Star
Copy link

Speaking of the batteries - it's worth mentioning the following topic (which, I believe, was named slightly differently in the past) and, specifically, comment 81:

Battery readings are sent when you put batteries in and when the state changes. The easiest way to get one is to remove and put back in one of the batteries.

Looks like it sends a battery state only when powered on or when the level drops by 20-25%

@Vlad-Star
Copy link

My comment above raised another question in my head - is there a way to retain the last known battery level somewhere in MQTT or HA, so it survives through the HA core reboots and version upgrades?

@rct
Copy link
Contributor

rct commented Sep 25, 2023

@kcpants - Thank you the very thorough analysis and all of you work on this.

Regarding frequency of battery updates, in case it helps give you some insights, here is every message I captured from my 5 Govee HS054 sensors from roughly Jan 2022 to Sept. 2023. These were purchased in 2021.

(the water leak reports were from cleaning people accidentally setting them off)

There are months between battery reports. It is possible there were missed messages. The gap from 2022-12 to 2023-08 for 6845 seems like at least some battery updates were missed.

===== 2749 =====
{"time" : "2022-06-20 23:36:01.184049", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "f54203a00352", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.916, "rssi" : -1.364, "snr" : 20.254, "noise" : -21.618}
{"time" : "2022-06-20 23:36:05.624008", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "f54203a00352", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -1.165, "snr" : 20.452, "noise" : -21.618}
{"time" : "2022-10-06 03:06:12.724904", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.900, "battery_mV" : 2880, "event" : "Battery Report", "code" : "f54203a50358", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.232, "snr" : 16.735, "noise" : -16.967}
{"time" : "2022-10-06 03:06:14.313760", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.900, "battery_mV" : 2880, "event" : "Battery Report", "code" : "f54203a50358", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.219, "snr" : 16.748, "noise" : -16.967}
{"time" : "2022-12-18 04:52:31.212035", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "f54203aa0346", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.229, "snr" : 16.840, "noise" : -17.069}
{"time" : "2022-12-18 04:52:32.752256", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "f54203aa0346", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.216, "snr" : 16.853, "noise" : -17.069}{"time" : "2023-09-12 14:49:55.350626", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.700, "battery_mV" : 2640, "event" : "Battery Report", "code" : "f54203b90342", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.957, "rssi" : -0.196, "snr" : 19.597, "noise" : -19.793}
{"time" : "2023-09-12 14:49:56.912062", "model" : "Govee-Water", "id" : 2749, "battery_ok" : 0.700, "battery_mV" : 2640, "event" : "Battery Report", "code" : "f54203b90342", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.917, "rssi" : -0.244, "snr" : 19.549, "noise" : -19.793}


===== 2752 =====
{"time" : "2022-06-01 17:49:15.476626", "model" : "Govee-Water", "id" : 2752, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "f53f03a00346", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.352, "snr" : 19.364, "noise" : -19.715}
{"time" : "2022-06-01 17:49:17.023539", "model" : "Govee-Water", "id" : 2752, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "f53f03a00346", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.447, "snr" : 19.269, "noise" : -19.715}
{"time" : "2022-09-08 10:49:08.102529", "model" : "Govee-Water", "id" : 2752, "battery_ok" : 0.900, "battery_mV" : 2880, "event" : "Battery Report", "code" : "f53f03a5034c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -7.618, "snr" : 5.608, "noise" : -13.225}
{"time" : "2022-09-08 10:49:09.380500", "model" : "Govee-Water", "id" : 2752, "battery_ok" : 0.900, "battery_mV" : 2880, "event" : "Battery Report", "code" : "f53f03a5034c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -3.161, "snr" : 10.229, "noise" : -13.390}
{"time" : "2022-11-21 08:36:03.091259", "model" : "Govee-Water", "id" : 2752, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "f53f03aa0352", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.217, "snr" : 20.637, "noise" : -20.854}
{"time" : "2022-11-21 08:36:04.601242", "model" : "Govee-Water", "id" : 2752, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "f53f03aa0352", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.882, "rssi" : -0.226, "snr" : 20.628, "noise" : -20.854}


===== 4865 =====
{"time" : "2022-06-21 18:17:31.879621", "model" : "Govee-Water", "id" : 4865, "event" : "Water Leak", "code" : "ecfe04040756", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.295, "snr" : 12.745, "noise" : -13.040}
{"time" : "2022-06-21 18:17:33.464188", "model" : "Govee-Water", "id" : 4865, "event" : "Water Leak", "code" : "ecfe04040756", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.268, "snr" : 12.772, "noise" : -13.040}
{"time" : "2022-07-27 16:20:08.186186", "model" : "Govee-Water", "id" : 4865, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "ecfe03a0034c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.308, "snr" : 13.270, "noise" : -13.577}
{"time" : "2022-07-27 16:20:09.766781", "model" : "Govee-Water", "id" : 4865, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "ecfe03a0034c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.169, "snr" : 13.408, "noise" : -13.577}
{"time" : "2022-12-29 18:32:18.939158", "model" : "Govee-Water", "id" : 4865, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "ecfe03aa0358", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.231, "snr" : 15.615, "noise" : -15.846}
{"time" : "2023-08-31 21:17:33.154341", "model" : "Govee-Water", "id" : 4865, "battery_ok" : 0.700, "battery_mV" : 2640, "event" : "Battery Report", "code" : "ecfe03b9035c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.916, "rssi" : -0.228, "snr" : 21.782, "noise" : -22.010}
{"time" : "2023-08-31 21:17:34.735351", "model" : "Govee-Water", "id" : 4865, "battery_ok" : 0.700, "battery_mV" : 2640, "event" : "Battery Report", "code" : "ecfe03b9035c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.247, "snr" : 21.763, "noise" : -22.010}



===== 5415 =====                                                                                                                                                                                                  {"time" : "2022-06-06 22:21:11.855248", "model" : "Govee-Water", "id" : 5415, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "ead803a00348", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.360, "snr" : 19.584, "noise" : -19.944}
{"time" : "2022-06-06 22:21:13.485383", "model" : "Govee-Water", "id" : 5415, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "ead803a00348", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.913, "rssi" : -0.293, "snr" : 19.651, "noise" : -19.944}
{"time" : "2022-09-15 00:38:36.663948", "model" : "Govee-Water", "id" : 5415, "battery_ok" : 0.900, "battery_mV" : 2880, "event" : "Battery Report", "code" : "ead803a50342", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.237, "snr" : 13.755, "noise" : -13.992}
{"time" : "2022-11-16 16:07:45.461189", "model" : "Govee-Water", "id" : 5415, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "ead803aa035c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.923, "rssi" : -0.244, "snr" : 20.746, "noise" : -20.989}
{"time" : "2022-11-16 16:07:46.915262", "model" : "Govee-Water", "id" : 5415, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "ead803aa035c", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.200, "snr" : 20.789, "noise" : -20.989}




===== 6845 =====
{"time" : "2022-02-09 09:35:40.590737", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.917, "rssi" : -0.228, "snr" : 17.534, "noise" : -17.762}
{"time" : "2022-02-09 09:35:45.688764", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.198, "snr" : 16.889, "noise" : -17.088}
{"time" : "2022-02-09 09:35:47.272503", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.922, "rssi" : -0.251, "snr" : 16.837, "noise" : -17.088}
{"time" : "2022-02-09 09:35:50.791769", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.248, "snr" : 17.567, "noise" : -17.815}
{"time" : "2022-02-09 09:35:52.375467", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.922, "rssi" : -0.210, "snr" : 17.605, "noise" : -17.815}
{"time" : "2022-02-09 09:35:55.896846", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.191, "snr" : 17.990, "noise" : -18.181}
{"time" : "2022-02-09 09:35:57.482015", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.177, "snr" : 18.005, "noise" : -18.181}
{"time" : "2022-02-09 09:36:02.467000", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.253, "snr" : 17.817, "noise" : -18.070}
{"time" : "2022-02-09 09:36:06.117663", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.224, "snr" : 17.695, "noise" : -17.919}
{"time" : "2022-02-09 09:36:07.658463", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.236, "snr" : 17.683, "noise" : -17.919}
{"time" : "2022-02-09 09:36:11.227131", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.922, "rssi" : -0.259, "snr" : 18.092, "noise" : -18.351}
{"time" : "2022-02-09 09:36:12.812427", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.215, "snr" : 18.135, "noise" : -18.351}
{"time" : "2022-02-09 09:36:16.336743", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.200, "snr" : 17.841, "noise" : -18.041}
{"time" : "2022-02-09 09:36:17.870505", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.224, "snr" : 17.817, "noise" : -18.041}
{"time" : "2022-02-09 09:36:23.009987", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -1.986, "snr" : 15.557, "noise" : -17.543}
{"time" : "2022-02-09 09:36:26.555627", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.238, "snr" : 17.836, "noise" : -18.075}
{"time" : "2022-02-09 09:36:28.132328", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.245, "snr" : 17.829, "noise" : -18.075}
{"time" : "2022-02-09 09:36:32.100732", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.143, "snr" : 9.226, "noise" : -9.370}
{"time" : "2022-02-09 09:36:36.775465", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.917, "rssi" : -0.255, "snr" : 17.718, "noise" : -17.973}
{"time" : "2022-02-09 09:36:41.884761", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.182, "snr" : 17.401, "noise" : -17.583}
{"time" : "2022-02-09 09:36:43.432709", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.195, "snr" : 17.389, "noise" : -17.583}
{"time" : "2022-02-09 09:36:46.997782", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.210, "snr" : 17.593, "noise" : -17.803}
{"time" : "2022-02-09 09:36:48.539063", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.201, "snr" : 17.602, "noise" : -17.803}
{"time" : "2022-02-09 09:41:38.758365", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.244, "snr" : 17.600, "noise" : -17.844}
{"time" : "2022-02-09 09:41:40.245557", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.185, "snr" : 17.659, "noise" : -17.844}
{"time" : "2022-04-20 08:56:38.140865", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.329, "snr" : 10.992, "noise" : -11.321}
{"time" : "2022-04-20 08:56:39.702256", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.288, "snr" : 11.033, "noise" : -11.321}
{"time" : "2022-04-20 08:56:43.238572", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.223, "snr" : 11.102, "noise" : -11.325}
{"time" : "2022-04-20 08:56:44.820988", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.923, "rssi" : -0.216, "snr" : 11.109, "noise" : -11.325}
{"time" : "2022-04-20 08:56:48.317462", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.915, "rssi" : -0.228, "snr" : 10.965, "noise" : -11.193}
{"time" : "2022-04-20 08:56:49.788915", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.245, "snr" : 10.949, "noise" : -11.193}
{"time" : "2022-04-20 08:56:53.442366", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.199, "snr" : 11.177, "noise" : -11.376}
{"time" : "2022-04-20 08:56:55.026855", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.917, "rssi" : -0.449, "snr" : 10.927, "noise" : -11.376}
{"time" : "2022-04-20 08:56:59.900489", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.222, "snr" : 11.072, "noise" : -11.294}
{"time" : "2022-04-20 08:57:01.493329", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.222, "snr" : 11.072, "noise" : -11.294}
{"time" : "2022-04-20 08:57:04.997058", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.240, "snr" : 11.026, "noise" : -11.267}
{"time" : "2022-04-20 08:57:06.589913", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.888, "rssi" : -6.708, "snr" : 4.559,"noise" : -11.267}
{"time" : "2022-04-20 08:57:10.068398", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.139, "snr" : 11.162, "noise" : -11.302}
{"time" : "2022-04-20 08:57:11.564699", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.248, "snr" : 11.054, "noise" : -11.302}
{"time" : "2022-04-20 08:57:17.547679", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.218, "snr" : 11.150, "noise" : -11.368}
{"time" : "2022-04-20 08:57:19.129424", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.960, "rssi" : -0.216, "snr" : 11.152, "noise" : -11.368}
{"time" : "2022-04-20 08:57:25.974699", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.239, "snr" : 11.044, "noise" : -11.282}
{"time" : "2022-04-20 08:57:27.556824", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.256, "snr" : 11.027, "noise" : -11.282}
{"time" : "2022-05-04 09:01:58.561777", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.917, "rssi" : -0.271, "snr" : 13.974, "noise" : -14.245}
{"time" : "2022-05-04 09:02:00.095679", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.917, "rssi" : -0.221, "snr" : 14.024, "noise" : -14.245}
{"time" : "2022-05-29 12:43:58.341224", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "e54203a00350", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.355, "snr" : 20.434, "noise" : -20.790}
{"time" : "2022-05-29 12:44:00.420661", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.950, "battery_mV" : 2940, "event" : "Battery Report", "code" : "e54203a00350", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.916, "rssi" : -0.496, "snr" : 20.293, "noise" : -20.790}
{"time" : "2022-07-27 09:34:58.660314", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.232, "snr" : 14.074, "noise" : -14.306}
{"time" : "2022-07-27 09:35:00.241803", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.205, "snr" : 14.100, "noise" : -14.306}
{"time" : "2022-07-27 09:35:03.758846", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.922, "rssi" : -0.245, "snr" : 14.334, "noise" : -14.580}
{"time" : "2022-07-27 09:35:05.328920", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.323, "snr" : 14.257, "noise" : -14.580}
{"time" : "2022-07-27 09:35:08.863466", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.185, "snr" : 13.889, "noise" : -14.075}
{"time" : "2022-07-27 09:35:13.970578", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.272, "snr" : 14.222, "noise" : -14.495}
{"time" : "2022-07-27 09:35:15.513134", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.269, "snr" : 14.226, "noise" : -14.495}
{"time" : "2022-07-27 09:35:18.038303", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.236, "snr" : 14.155, "noise" : -14.391}
{"time" : "2022-07-27 09:35:19.619627", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.176, "snr" : 14.216, "noise" : -14.391}
{"time" : "2022-07-27 09:35:23.016219", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.179, "snr" : 14.271, "noise" : -14.450}
{"time" : "2022-07-27 09:35:24.529336", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.149, "snr" : 14.301, "noise" : -14.450}
{"time" : "2022-07-27 09:35:30.708835", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.218, "snr" : 14.212, "noise" : -14.430}
{"time" : "2022-07-27 09:35:32.243085", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.254, "snr" : 14.176, "noise" : -14.430}
{"time" : "2022-07-27 09:35:36.619644", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.201, "snr" : 14.116, "noise" : -14.317}
{"time" : "2022-07-27 09:35:38.201276", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.285, "snr" : 14.032, "noise" : -14.317}
{"time" : "2022-07-27 09:35:40.189778", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.203, "snr" : 14.191, "noise" : -14.394}
{"time" : "2022-07-27 09:35:41.784934", "model" : "Govee-Water", "id" : 6845, "event" : "Water Leak", "code" : "e5420404074a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.184, "snr" : 14.210, "noise" : -14.394}
{"time" : "2022-08-28 09:51:15.524451", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.900, "battery_mV" : 2880, "event" : "Battery Report", "code" : "e54203a5035a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.931, "rssi" : -0.310, "snr" : 21.889, "noise" : -22.199}
{"time" : "2022-08-28 09:51:18.054686", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.900, "battery_mV" : 2880, "event" : "Battery Report", "code" : "e54203a5035a", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.922, "rssi" : -0.275, "snr" : 21.923, "noise" : -22.199}
{"time" : "2022-10-30 05:27:34.151162", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "e54203aa0344", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.919, "rssi" : -0.193, "snr" : 20.155, "noise" : -20.348}
{"time" : "2022-10-30 05:27:35.732378", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.850, "battery_mV" : 2820, "event" : "Battery Report", "code" : "e54203aa0344", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.921, "rssi" : -0.242, "snr" : 20.106, "noise" : -20.348}
{"time" : "2022-12-22 06:35:15.300505", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.800, "battery_mV" : 2760, "event" : "Battery Report", "code" : "e54203af034e", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.918, "rssi" : -0.227, "snr" : 15.489, "noise" : -15.716}
{"time" : "2023-08-24 18:47:04.049147", "model" : "Govee-Water", "id" : 6845, "battery_ok" : 0.650, "battery_mV" : 2580, "event" : "Battery Report", "code" : "e54203be034e", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.183, "snr" : 15.076, "noise" : -15.259}

@rct
Copy link
Contributor

rct commented Sep 25, 2023

@Vlad-Star wrote:

My comment above raised another question in my head - is there a way to retain the last known battery level somewhere in MQTT or HA, so it survives through the HA core reboots and version upgrades?

The MQTT retain flag implementation in rtl_433 is currently very course grained, so it is essentially on or off. Given the infrequency of Govee updates, you'd like the retain flag to be on for all Govee related messages.

But if you turn retain on, any messages that are from false positives will stay around in your MQTT broker until you delete them.

This might be a case where using something like mqtt_relay with customization would be more useful rather than forwarding everything directly from rtl_433 to MQTT.

I commented in another thread about how I'd like meta data about devices to be retained and would be a good use case for a companion app.

The quickest, probably easiest solution would be to use a trigger in Home Assistant, since that data is retained across Hass restarts.

@kcpants
Copy link
Contributor Author

kcpants commented Sep 25, 2023

@rct Thanks for the battery message data!

All this discussion is great but I feel like it may be growing beyond the scope of the contents of this pull request so, in the interest of getting this merged, I'd like to try to refocus things. Although this change is part of a larger effort to make the Govee leak sensors more user-friendly, the important aspects of this particular fix seem to be the naming convention for json keys corresponding to binary sensors (which up to now have not been defined in rtl_433) and, to a lesser extent, the mapping of the "Button Press" message to detect_wet:0.

To summarize, this change implicitly proposes a general naming convention of detect_<noun that binary sensor detects> for all devices whose state is represented as a binary sensor in HA and explicitly defines the json key detect_wet for binary moisture sensors. The main Govee-specific question in this code is the mapping of "Button Press" to detect_wet: 0.

It seems like folks are happy with detect_wet and feel that mapping "Button Pressed" to detect_wet: 0 is reasonable given the behavior of the sensor as well as how it has been integrated with Home Assistant by the community at large. It would be great if further comments could be limited to these topics or the bullet points in my pull request description comment at the very top of the PR.

I propose that if another few days goes by without significant concerns regarding the main points in this fix it can be integrated and I will move on to the change in rtl_433_mqtt_hass.py.

@gdt
Copy link
Collaborator

gdt commented Sep 25, 2023

mqtt: Big picture, we need to separate "send json decode to receiver" from "bridge to some other system with defined semantics". The in-rtl_433 mqtt is only suitable for the first.

As for button pressing creating detect_wet:0, is this documented by Govee? Is the button available for other use as a separate channel? If the sensor is still wet, will it send the button-press code, and then send another wet code in a few seconds? It really seems like button press should be mapped to a button-press event and the next-level software should sort out semantics. But if the convention is that the button should be pressed by a human when they decide it is no longer wet, I can see it. Overall I think this device is badly designed and I don't mind adapting to make it useful, but there should be comments that this is irregular to work around a bad design.

@Vlad-Star
Copy link

As for button pressing creating detect_wet:0, is this documented by Govee?

This is what the H5054 manual says:

  • Push Button: Pause alarm/ Adjust volume.
  • Volume adjustment Adjust alarm volume by pressing the button twice within 1 s and the indicator will flash quickly. The volume level cycles through high – medium – low – mute, and default volume is high.
  • Alarm clear Press the Push Button to pause alarm for 5s. Remove the device from water and wipe it up to stop alarm completely.

@kcpants
Copy link
Contributor Author

kcpants commented Sep 25, 2023

If the sensor is still wet, will it send the button-press code, and then send another wet code in a few seconds?

I performed a few more manual tests to see how the button behaves in conjunction with the alarm. When the sensor is wet (contacts shorted) the button does silence the alarm for ~5 seconds as described in the manual. When the button is used for this function no "Button Press" message is sent. Additionally, while the sensor was wet I was unable to change the volume by pressing the button twice. In other words, based on my testing, the "Button Press" message is only sent when the sensor is dry (contacts open).

It really seems like button press should be mapped to a button-press event and the next-level software should sort out semantics. But if the convention is that the button should be pressed by a human when they decide it is no longer wet, I can see it. Overall I think this device is badly designed and I don't mind adapting to make it useful, but there should be comments that this is irregular to work around a bad design.

Few thoughts on this:

First, note that this change does not remove the original event key so the existing "Button Press" message will still be available for use in the json output.

Second, the fact that the button press message is only transmitted when the device is dry makes me think it's even more appropriate to include detect_wet: 0 in the json output when it is pressed. That the button is also used to change volume is a little unfortunate but ultimately doesn't seem too problematic because it still seems correct to assume the sensor is dry when the volume is being adjusted (since it cannot be adjusted when wet and the alarm is sounding).

Third, keep in mind that there is always some "semantic" mapping when converting any device's raw signal into json that will then be sent to MQTT. Correct me if I'm wrong but it looks like most (if not all) decoders are implemented by reverse engineering captured signals sent by devices, not from a manufacturer spec sheet. As such, the json keys used by decoders are not prescribed by the device manufacturer, they are chosen by the person who implements the decoder for the device. In this case, I think the original author of the decoder made an unfortunate choice of mapping everything to event with a few additional battery specific keys as seen below:

rtl_433/src/devices/govee.c

Lines 375 to 384 in 07368e7

data_t *data = data_make(
"model", "", DATA_STRING, "Govee-Water",
"id" , "", DATA_INT, id,
"battery_ok", "Battery level", DATA_COND, battery >= 0, DATA_DOUBLE, battery_level,
"battery_mV", "Battery", DATA_COND, battery >= 0, DATA_FORMAT, "%d mV", DATA_INT, battery_mv,
"event", "", DATA_STRING, event_str,
"leak_num", "Leak Num", DATA_COND, leak_num >= 0, DATA_INT, leak_num,
"code", "Raw Code", DATA_STRING, code_str,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);

As a related example drawn from the code snippet above, the govee decoder author decided to include the battery_ok key in the json output whenever any message containing battery data is received (battery >= 0) regardless of what the battery level actually is and further they chose to send the battery level as a double (presumably representing the percent charged) as the value associated with that key. This goes against how the battery_ok key is used by other decoder authors who generally transmit either zero or one depending on whether the battery is low or not (as reported by the device). You can find many examples of this usage by searching for battery_ok in the repo; I've linked to one below for convenience. Because govee doesn't seem to send its own binary battery_ok signal the decoder author synthesized their own battery_ok key-value pair from the data that govee does send (this might also be worth changing to align with other devices but I'd like to defer that to a future discussion / pull request):
"battery_ok", "Battery_OK", DATA_INT, !battery_low,

As always, I'm happy to add comments if requested and although I do agree that the behavior of this device is unusual and suboptimal I don't believe including detect_wet: 0 alongside event: "Button Press" in the json output diverges too much from the intended purpose of the decoders.

@rct
Copy link
Contributor

rct commented Sep 25, 2023

I'm not seeing detect_wet == 0, because it is only implemented for the older decoder. I have sensors purchased in 2021, but maybe these are the older version?

rtl_433 version nightly-61-g403abe95 branch kcpants_standardize_govee_keys at 202309191230 inputs file rtl_tcp RTL-SDR with TLS
{"time" : "2023-09-25 18:06:23.005540", "model" : "Govee-Water", "id" : 2749, "event" : "Button Press",
"code" : "f54205050748", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.247, "snr" : 19.597, "noise" : -19.844}
{"time" : "2023-09-25 18:06:28.757310", "model" : "Govee-Water", "id" : 2749, "event" : "Button Press",
"code" : "f54205050748", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.917, "rssi" : -0.224, "snr" : 19.476, "noise" : -19.700}
{"time" : "2023-09-25 18:09:21.323310", "enabled" : 196, "since" : "2023-09-25T18:04:21", "frames" : {"count" : 282, "fsk" : 0, "events" : 172}, "stats" : [{"device" : 20, "name" : "Ambient Weather F007TH, TFA 30.3208.02, SwitchDocLabs F016TH temperature sensor", "events" : 354, "ok" : 39, "messages" : 39, "fail_other" : 314, "fail_mic" : 1}, {"device" : 40, "name" : "Acurite 592TXR Temp/Humidity, 592TX Temp, 5n1 Weather Station, 6045 Lightning, 899 Rain, 3N1, Atlas", "events" : 314, "ok" : 137, "messages" : 404, "fail_other" : 174, "abort_length" : 1, "fail_sanity" : 2}, {"device" : 192, "name" : "Govee Water Leak Detector H5054, Door Contact Sensor B5023", "events" : 285, "ok" : 2, "messages" : 2, "abort_length" : 96, "abort_early" : 187}]}

$ egrep wet merbanan/rtl_433/src/devices/govee.c
    int wet = -1;
        wet = 0;
        wet = 1;
            "detect_wet",   "",                 DATA_COND,   wet >= 0, DATA_INT, wet,
        "detect_wet",

I haven't looked at this too closely, is there any reason the key standardization and detect_wet logic should apply to both decoders?

@kcpants
Copy link
Contributor Author

kcpants commented Sep 25, 2023

I haven't looked at this too closely, is there any reason the key standardization and detect_wet logic should apply to both decoders

There is no reason not to add detect_wet to the older decoder in principle. However, there are two practical reasons why I personally did not make the change.

First, I don't own any of the older devices so it would be impossible for me to thoroughly test a fix in the older decoder. Having worked as a software developer for several years, even if the change to the old decoder were straightforward, I would be very reluctant to merge any untested code.

Second, the older decoder actually handles two govee devices so it's not quite as simple as copy-pasting the code that's currently in the PR up to the other decoder. In particular, it's important that the detect_wet key not appear in the json output of the Govee door sensor (since that would result in the door sensor being incorrectly discovered as a moisture sensor). Ideally, someone who owns both the old Govee leak detector as well as the door sensor could implement and test the change to verify that it works and detect_wet does not appear for door sensors.

@gdt
Copy link
Collaborator

gdt commented Sep 26, 2023

Given that the button doesn't send a button event when it's still wet, I'm ok with button getting mapped to detect_wet:0 as a binary sensor report, and a button press event (although if we're assigning semantics, I'm a little iffy on the button press event).

As for battery_ok, I am confused this minute about what the rules are, between rtl_433 and home assistant. The concepts are binary sensor, and 0-100%. I'd have to dig in and figure out the rules. I think it's ok to fix the other issues and let that be and address battery separately.

@rct
Copy link
Contributor

rct commented Sep 26, 2023

I apparently have older hardware and can test.

If breaking changes are going to be introduced in the name of standardizing and fixing things, it seems like it would be good to make the older and newer govee leak detectors behave the same.

Edit: - I don't have the govee door sensor, but if there aren't samples in rtl_433_tests (or a PR) maybe we can get someone who has one to test, but clearly the two could have different "data makes" based on the event type.

@kcpants
Copy link
Contributor Author

kcpants commented Sep 26, 2023

From a coding perspective my main concern when I looked at the older decoder was the output_fields which are shared between the two sensors:

rtl_433/src/devices/govee.c

Lines 241 to 261 in 07368e7

static char const *const output_fields[] = {
"model",
"id",
"battery_ok",
"battery_mV",
"event",
"code",
"mic",
NULL,
};
r_device const govee = {
.name = "Govee Water Leak Detector H5054, Door Contact Sensor B5023",
.modulation = OOK_PULSE_PWM,
.short_width = 440, // Threshold between short and long pulse [us]
.long_width = 940, // Maximum gap size before new row of bits [us]
.gap_limit = 900, // Maximum gap size before new row of bits [us]
.reset_limit = 9000, // Maximum gap size before End Of Message [us]
.decode_fn = &govee_decode,
.fields = output_fields,
};

I was unsure if adding detect_wet to the shared output_fields array would result in the new field incorrectly appearing for the door sensor. On the surface, it looks to be safe assuming that the DATA_COND for detect_wet is only true for the leak sensors and that a key is omitted when its DATA_COND is false but didn't feel comfortable making the change without being able to test it.

If breaking changes are going to be introduced in the name of standardizing and fixing things, it seems like it would be good to make the older and newer govee leak detectors behave the same.

I agree it would be ideal to add the new key to the old decoder. I'd like to emphasize though that no breaking changes are being proposed. The change adds a new field to the json output but no existing fields are being removed or modified so any setups that rely on the event key today will continue to work. Furthermore, I don't believe the event key can ever realistically be removed or modified without causing a lot of headaches for people that already rely on it.

As a volunteer developer, I'd be happy to help with the modifications for the old decoder but would prefer someone who actually owns the device to create and submit a separate pull request. I'm certain the original authors of the decoder code possess the physical devices they are writing code for so this seems like a reasonable restriction.

@rct
Copy link
Contributor

rct commented Sep 26, 2023

I believe the output fields are only used for figuring out what columns should go into CSV output. So yes adding any changes to that list potentially breaks someone's CSV usage if they don't pay attention to the column headings. CSV output is pretty fragile, it is only practical in limited cases. Anyone feeding data to a system like Home Assistant is likely not using CSV.

With regards to being a volunteer, we're all volunteers here.

I don't want to stand in the way of this being merged -- that's someone else's decision. But I do think given your new decoder still identifies as Model == "Govee Water" that it will be confusing to have two devices with the same identifier and different semantics.

One bit of feedback that I believe is still open is that @gdt requested that you document the detect_wet state that has been introduced because without reading the comments in this PR that logic is going to be fairly opaque.

@rct
Copy link
Contributor

rct commented Sep 26, 2023

@kcpants - I don't think I can push an additional commit to your branch/PR.

Here's the same mod for the old decoder: rct@eddb4f3

Tested with my older H5054 water detector.

As far as intentional changes to the Govee contact sensor, if I'm understanding things correctly

  • As discussed Govee CSV users will find an additional field in all output
  • There is no chance for the contact open message that detect_wet will be included
  • The Govee contact is flawed design like the water sensor, in that it does not send a closed event, so no change there.
  • If the contact sensor has a button, there will now but an additional detect_wet field, howver, the decoder is not currently able to detect the model on a button press, so any button press today would have been sent as model == "Govee Water" instead.

So I think this is acceptable. However, I'm tagging @skilau - the author of the PR with the contact sensor changes for feedback.

@kcpants
Copy link
Contributor Author

kcpants commented Sep 26, 2023

@rct Nice work, you beat me to the fix! I also just finished prepping a diff with similar changes for you to apply and test.

govee.patch

After looking through the code again my other big concern was also whether the door sensor has a button and sends its own "Button Press" messages. As you observed, the code snippet below seems to imply that the only message sent by the door contact sensor is "Open" (event == 0xe7f) because model_num is assigned to GOVEE_WATER at the top of the function and only changed to GOVEE_CONTACT if an open message is received. If this assumption is valid and the output_fields are only applicable to CSV as you mentioned then the patch should be safe and could be merged after this one.

rtl_433/src/devices/govee.c

Lines 202 to 234 in 07368e7

if (event == 0xafa) {
event_str = "Button Press";
}
else if (event == 0xbfb) {
event_str = "Water Leak";
}
else if (event_type == 0xc) {
event_str = "Battery Report";
}
else if (event == 0xdfd) {
event_str = "Heartbeat";
}
else if (event == 0xe7f) {
// Only sent by the Contact sensor
model_num = GOVEE_CONTACT;
event_str = "Open";
}
else {
event_str = "Unknown";
}
/* clang-format off */
data_t *data = data_make(
"model", "", DATA_COND, model_num == GOVEE_WATER, DATA_STRING, "Govee-Water",
"model", "", DATA_COND, model_num == GOVEE_CONTACT, DATA_STRING, "Govee-Contact",
"id" , "", DATA_INT, id,
"battery_ok", "Battery level", DATA_COND, battery, DATA_DOUBLE, battery_level,
"battery_mV", "Battery", DATA_COND, battery, DATA_FORMAT, "%d mV", DATA_INT, battery_mv,
"event", "", DATA_STRING, event_str,
"code", "Raw Code", DATA_STRING, code_str,
"mic", "Integrity", DATA_STRING, "PARITY",
NULL);
/* clang-format on */

With respect to my comment regarding volunteering, I didn't mean to imply that others are not also volunteering (very sorry if it was interpreted that way!). Just that as volunteers we all have a right to decide how much responsibility we are willing to assume. By submitting this PR I am accountable for the code in it and, if it caused any problems down the line, I feel it would be my duty to fix them if at all possible. I'm comfortable taking this on for code I can fully test but not for code that I would be dependent on others (who I may not be able to coordinate with in the future) to validate.

@rct
Copy link
Contributor

rct commented Sep 26, 2023

@kcpants - You patch looks nearly identical to my commit, other than:

  1. You reversed the order of event and detect wet in your new patch, rather than keeping it the same as your new decoder.
  2. missing a comment to explain what's going on:

As @gdt requested, you should have a similar comment somewhere in the code to explain what's going on because it can't be inferred from the code.

With regards to not wanting to make changes to code for devices you can't test, I understand that and agree with the sentiment That's what the tests repo was far before it fell out of use. But I and others have had to make changes after doing some due diligence in order to make progress. For example see src/devices/acurite.c

While your concern for the Govee contact sensor users is admirable, I see it as somewhat likely that there will be future issues opened against rtl_433 because there being two different behaviors for devices that both identify themselves as "Govee Water".

@gdt
Copy link
Collaborator

gdt commented Sep 26, 2023

I see these devices as problematic and lean to rip off the bandaid and fix, not worrying about compat so much. I agree that inconsistency is worse than risk of untested trouble.

@skilau
Copy link
Contributor

skilau commented Sep 26, 2023

Hi all!
Thanks for taking the reigns on this driver, as Govee seems to have continued releasing newer versions of the Sensors, with (hopefully) more well thought out ways of doing things.

About the door/contact sensors, I do still have them, but I do not use them...
I kind of tossed them in a drawer and forgot about them, since without having a "door/contact close" message, they really don't serve a purpose for me.
To answer a question posed above, the door/contact sensors do NOT have a button on them.
So there is no way to reset/test them, they only send an Event when the sensor moves far enough away from the magnet.
And as mentioned, they do NOT send an Event when you move the magnet back close to the sensor again. (Very annoying!)
Perhaps Govee has made a new version of these sensors, I don't know. But the ones I have are definitely of limited value/use.

@kcpants kcpants force-pushed the standardize_govee_keys branch from 403abe9 to c70d79f Compare September 26, 2023 20:43
@zuckschwerdt
Copy link
Collaborator

send the battery level as a double (presumably representing the percent charged) as the value associated with that key.

This is expected behaviour. The battery_ok value is always a floating point value (0-100%) but as seen in most decoders it's truncated to a simple 0/1. https://triq.org/rtl_433/DATA_FORMAT.html#common-device-data

@zuckschwerdt
Copy link
Collaborator

if adding detect_wet to the shared output_fields array would result in the new field incorrectly appearing

The output_fields labels are just used as CSV column headers. Worst case you get more columns than needed.

@kcpants kcpants force-pushed the standardize_govee_keys branch from c70d79f to 4685f50 Compare September 26, 2023 20:59
@kcpants
Copy link
Contributor Author

kcpants commented Sep 26, 2023

Added the fix for the old decoder as well as a slightly more detailed version of the suggested comment to both decoders. I also removed the output_fields_5054 array I had defined for the new decoder since in the current version of the change the output_fields array used by the old decoder is again identical to the one needed by the new decoder. I will compile and test the latest version with my sensors to be sure it still works.

As I've said, I'd prefer if the changes for the two decoders were merged in two separate pull requests but I'm fine with piggy-backing them if others are taking responsibility for the older sensors (ultimately, I'm sure these changes will save many hours of people's lives and am happy to see both get fixed).

@kcpants
Copy link
Contributor Author

kcpants commented Sep 26, 2023

Tests complete. Latest rtl_433 binary (compiled for 64-bit arm RPi 4) as well as the json output it produces included below for posterity.

rtl_433.tar.gz

It also might be worthwhile for someone who owns the older revision of the sensors to verify that the "button press" behaves the same way as it does on the newer sensor (i.e. short the sensor with water, press the button while the alarm is sounding and verify that no "button press" message is received).

Manually Shorted Sensor Contacts (detect_wet = 1)

{
  "time": "2023-09-26T17:29:32.901044-0400",
  "model": "Govee-Water",
  "id": 21723,
  "event": "Water Leak",
  "detect_wet": 1,
  "leak_num": 32,
  "code": "54db3220632a",
  "mic": "CRC"
}

Button Press (detect_wet = 0)

{
  "time": "2023-09-26T17:31:01.835264-0400",
  "model": "Govee-Water",
  "id": 21723,
  "event": "Button Press",
  "detect_wet": 0,
  "code": "54db30543b5b",
  "mic": "CRC"
}

@rct
Copy link
Contributor

rct commented Sep 26, 2023

It also might be worthwhile for someone who owns the older revision of the sensors to verify that the "button press" behaves the same way as it does on the newer sensor (i.e. short the sensor with water, press the button while the alarm is sounding and verify that no "button press" message is received).

Sorry I should have mentioned sooner that I confirmed that earlier before I made my code change.

Anyway here's the output with the head of the current PR:

rtl_433 version nightly-61-g4685f50f branch standardize_govee_keys at 202309261658 inputs file rtl_tcp RTL-SDR with TLS
{"time" : "2023-09-26 18:11:59.817023", "model" : "Govee-Water", "id" : 2749, "detect_wet" : 1, "event"
: "Water Leak", "code" : "f54204040748", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.916, "rssi" : -0.412, "snr" : 17.765, "noise" : -18.177}
{"time" : "2023-09-26 18:12:01.179227", "model" : "Govee-Water", "id" : 2749, "detect_wet" : 1, "event"
: "Water Leak", "code" : "f54204040748", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.257, "snr" : 18.302, "noise" : -18.559}
{"time" : "2023-09-26 18:12:04.349728", "model" : "Govee-Water", "id" : 2749, "detect_wet" : 1, "event"
: "Water Leak", "code" : "f54204040748", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.934, "rssi" : -0.262, "snr" : 18.516, "noise" : -18.778}
{"time" : "2023-09-26 18:12:25.793079", "model" : "Govee-Water", "id" : 2749, "detect_wet" : 0, "event"
: "Button Press", "code" : "f54205050748", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.929, "rssi" : -1.451, "snr" : 15.690, "noise" : -17.141}
{"time" : "2023-09-26 18:13:44.748918", "model" : "Govee-Water", "id" : 2749, "detect_wet" : 0, "event"
: "Button Press", "code" : "f54205050748", "mic" : "PARITY", "mod" : "ASK", "freq" : 433.920, "rssi" : -0.163, "snr" : 17.735, "noise" : -17.898}

Looks good to me. Thanks for all your work on this!

LGTM

@zuckschwerdt
Copy link
Collaborator

Sorry, late entry for the naming scheme: detect_wet could also be named sense_wet which would sound somewhat more natural? But really no difference, I just had to get it out of my head.

Another contender would be condition_wet -- it could possibly fit more binary sensors use cases, e.g. condition_armed for some security device, detect_armed would be strange there.
OTOH condition_wet: 0 sounds a bit silly, the current detect_wet: 0 sounds better to me.

@gdt
Copy link
Collaborator

gdt commented Sep 27, 2023

I see detect and sense as almost synonyms, but sense is more about how and detect is more the result.

@rct
Copy link
Contributor

rct commented Sep 27, 2023

I don’t have a strong feeling about either detect or sense, but I think detect is seems to match the language used in Home Assistant. So I’d stick with that.

@gdt
Copy link
Collaborator

gdt commented Sep 27, 2023

I think we're ready to merge and @rct said so as well. So @zuckschwerdt go ahead if you concur.

@zuckschwerdt
Copy link
Collaborator

Big thanks to all of you for taking the time for this discussion and make this such a polished feature.

@zuckschwerdt zuckschwerdt merged commit 0272408 into merbanan:master Sep 27, 2023
5 checks passed
andrewjw pushed a commit to andrewjw/rtl_433 that referenced this pull request Sep 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants