From 3ecdf1fb8dd67c504d1d3adbd16962d776a40dbd Mon Sep 17 00:00:00 2001 From: agessaman Date: Wed, 4 Nov 2026 16:46:27 -0800 Subject: [PATCH] =?UTF-8?q?fix(mqtt=5Fweather):=20don't=20render=200=C2=B0?= =?UTF-8?q?C=20as=20-0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rounding a reading just below freezing (31.95°F) gives -0.0, which printed as "-0.0°C". --- CHANGELOG.md | 4 ++-- modules/clients/mqtt_weather.py | 3 ++- tests/unit/test_mqtt_weather.py | 8 ++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4eb3a9..7a0cf64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,9 +51,9 @@ semantic versioning. ### Fixed -- MQTT weather sources (`wx custom.mqtt_weather.*`) in `json_template` mode no longer put a long Celsius value on air: the default template rendered `61.3°F (16.27777777777778°C)`. I now round `{temperature_c}` to one decimal, matching the precision of the °F reading, so it reads `16.3°C`. Format specs such as `{temperature_c:.0f}` still work, and a missing reading still renders as empty rather than failing the template. +- MQTT weather sources (`wx custom.mqtt_weather.*`) in `json_template` mode no longer put a long Celsius value on air: the default template rendered `61.3°F (16.27777777777778°C)`. I now round `{temperature_c}` to one decimal, matching the precision of the °F reading, so it reads `16.3°C` (and 31.95°F reads `0.0°C`, not `-0.0°C`). Format specs such as `{temperature_c:.0f}` still work, and a missing reading still renders as empty rather than failing the template. - The Nix flake builds again on current nixpkgs. The locked meshcore-cli (1.3.11) caps `bleak` below 2.0 and nixpkgs now ships 3.0.2, so its runtime dependency check failed; the flake relaxes that one pin ([#352](https://github.com/agessaman/meshcore-bot/issues/352)). -- The weather service's Blitzortung lightning feed backs off when the broker keeps dropping it: 5 seconds after a drop, doubling to 5 minutes, and back to 5 seconds after a connection that lasts a minute. It used to reconnect every second, logging two lines each time ([#362](https://github.com/agessaman/meshcore-bot/issues/362)). It only logs "Connected" once the broker accepts the session, says why it is reconnecting, and gives each connection its own client ID. Strikes inside `blitz_area_*` also reach the lightning handler now. They arrive on the MQTT library's own thread, where scheduling the handler failed and was logged only at debug level, so they were dropped. +- The weather service's Blitzortung lightning feed backs off when the broker keeps dropping it: 5 seconds after a drop, doubling to 5 minutes, and back to 5 seconds after a connection that lasts a minute. It used to reconnect every second, logging two lines each time ([#362](https://github.com/agessaman/meshcore-bot/issues/362)). It only logs "Connected" once the broker accepts the session, says why it is reconnecting, and gives each connection its own client ID. Strikes inside `blitz_area_*` also reach the lightning handler now. They arrive on the MQTT library's own thread, where scheduling the handler failed and was logged only at debug level, so they were dropped. Strikes that arrive while an alert is being sent are kept for the next count instead of being cleared with the batch. - The web viewer's Email & Notifications panel has an "Allow a local or private-network SMTP server" checkbox. `allow_local_smtp` is stored in the bot database, not `config.ini`, and no control set it, so a local relay such as Postfix could not be used for the nightly email or radio alerts ([#361](https://github.com/agessaman/meshcore-bot/issues/361)). - A repeater heard twice in a row no longer posts two "new repeater" messages to Discord and Telegram. The prefix-collision service decided to announce a key and only recorded that key after the posts returned, so a second NEW_CONTACT while the first post was still in flight sent again (and the same gap double-posted a prefix-collision alert onto the mesh, which the bridges delivered twice). It now records the key before it sends. - The greeter's warmup (`rollout_days`) runs once, the first time the greeter starts. A restart more than a day after the previous warmup ended used to open another one, and anyone who spoke during it was marked greeted and never welcomed. Ending a warmup, or it expiring, no longer starts a new one on the next boot. diff --git a/modules/clients/mqtt_weather.py b/modules/clients/mqtt_weather.py index c477bc3..0b10bb9 100644 --- a/modules/clients/mqtt_weather.py +++ b/modules/clients/mqtt_weather.py @@ -370,7 +370,8 @@ def format_mqtt_weather_payload( if temp_f is not None: # Rounded to one decimal (the usual precision of the °F reading) so the bare # {temperature_c} placeholder renders e.g. 16.3 rather than 16.27777777777778. - temp_c = round((temp_f - 32.0) * 5.0 / 9.0, 1) + # Adding 0.0 turns -0.0 (31.95°F) into 0.0 so it doesn't print as "-0.0". + temp_c = round((temp_f - 32.0) * 5.0 / 9.0, 1) + 0.0 uptime_i = _coerce_nonnegative_int(data.get("uptime_s")) diff --git a/tests/unit/test_mqtt_weather.py b/tests/unit/test_mqtt_weather.py index 02eb720..2029f7a 100644 --- a/tests/unit/test_mqtt_weather.py +++ b/tests/unit/test_mqtt_weather.py @@ -269,3 +269,11 @@ def test_mqtt_weather_display_no_cache() -> None: text2, err2 = mqtt_weather_display_for_topic("missing", None, fmt) assert text2 is None assert err2 == "no_cache" + + +def test_celsius_just_below_freezing_is_not_negative_zero() -> None: + fmt = _default_json_template_fmt() + raw = json.dumps({"time": "12:00", "temperature_F": 31.95, "humidity": 70}).encode() + text, err = format_mqtt_weather_payload(raw, fmt) + assert err is None + assert text == "12:00 | 31.95°F (0.0°C) | RH 70.0%"