Skip to content

Mqtt optimisations and TZ fixes#1205

Merged
jokob-sk merged 8 commits intonetalertx:mainfrom
ingoratsdorf:mqtt-optimisations
Sep 28, 2025
Merged

Mqtt optimisations and TZ fixes#1205
jokob-sk merged 8 commits intonetalertx:mainfrom
ingoratsdorf:mqtt-optimisations

Conversation

@ingoratsdorf
Copy link
Contributor

@ingoratsdorf ingoratsdorf commented Sep 28, 2025

A bit of cleanup and optimisation work.
Fixes bug #1204
Deleted unused imports
Added source code documentation

Summary by CodeRabbit

  • Bug Fixes

    • Corrected timestamp timezone handling using the configured timezone for accurate event times.
    • Prevented publish attempts when disconnected; publish now returns failure instead of error.
    • Non-string payloads are now auto-converted to JSON for reliable message delivery.
  • Refactor

    • Streamlined MQTT logging with clearer, leveled messages and aggregated outputs for easier troubleshooting.
    • Consistent payload/topic handling and sensor message initialization for improved reliability (no behavior changes).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 28, 2025

Walkthrough

Refactors the MQTT publisher: replaces explicit comparisons with truthiness checks, adjusts logging levels/messages, returns False when disconnected, localizes naive timestamps with conf.tz.localize, reorganizes message/topic/payload assembly and initializer formatting, plus whitespace and comment cleanup.

Changes

Cohort / File(s) Summary
MQTT Publisher module
front/plugins/_publisher_mqtt/mqtt.py
Reworked config checks to use truthiness; updated connection/client conditionals and reconnection logic; publish_mqtt converts non-strings to JSON and returns False if not connected; logging levels and multi-line log formatting adjusted; timestamp handling uses conf.tz.localize; sensor/message initialization and topic/payload assembly reformatted and clarified; assorted docstrings, comments, whitespace, and line-length cleanups.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App as Publisher
  participant Cfg as Config
  participant TZ as conf.tz
  participant MQTT as MQTT Client

  App->>Cfg: check_config()
  alt config truthy
    App->>MQTT: check connected (truthiness)
    alt connected
      App->>App: ensure message is dict/string
      App->>App: convert non-strings → JSON
      App->>TZ: localize naive timestamp via tz.localize
      App->>MQTT: publish(topic, payload, qos, retain)
      MQTT-->>App: publish result
      App-->>App: log (debug/minimal)
    else not connected
      App-->>App: log not-connected (minimal)
      App-->>App: return False
    end
  else config missing/false
    App-->>App: log config issue
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitch my ears at MQTT breeze,
Timestamps tucked where time agrees,
If the client naps, I say “False” and hop,
Logs whisper softly, tidy on top,
A rabbit's nod to cleaner ops. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly captures the core purpose of the pull request by calling out both MQTT optimisations and timezone handling fixes, which aligns with the major refactoring and tz localization adjustments described in the changeset.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d9feddd and af986aa.

📒 Files selected for processing (1)
  • front/plugins/_publisher_mqtt/mqtt.py (15 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
front/plugins/*/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

front/plugins/*/*.py: Plugin scripts must log only via mylog(), never using print()
Collect results with Plugin_Objects.add_object(...) during processing and call plugin_objects.write_result_file() exactly once at the end
Write results exclusively to /app/log/plugins/last_result..log using Plugin_Objects; do not write ad‑hoc result files
When creating/updating devices, define database_column_definitions and ensure watched fields map to notifications
Validate and normalize MAC addresses (use helper normalize_mac) before any DB write or object emission

Files:

  • front/plugins/_publisher_mqtt/mqtt.py
{server/**/*.py,front/plugins/*/*.py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

{server/**/*.py,front/plugins/*/*.py}: Never hardcode ports or secrets; define settings once (core ccd() or plugin manifest) and read via get_setting_value() everywhere
Use logger.mylog(level, [message]) with levels none/minimal/verbose/debug/trace; keep verbose output concise unless debugging
Reuse helpers in helper.py (timeNowTZ, normalize_mac, sanitizers) and prefer server/db/db_helper.py over raw SQL in new paths

Files:

  • front/plugins/_publisher_mqtt/mqtt.py
🧬 Code graph analysis (1)
front/plugins/_publisher_mqtt/mqtt.py (4)
server/plugin_utils.py (1)
  • getPluginObject (246-276)
server/logger.py (3)
  • mylog (86-89)
  • Logger (55-93)
  • timeNowTZ (16-20)
server/helper.py (5)
  • timeNowTZ (35-45)
  • get_setting_value (358-411)
  • bytes_to_string (603-607)
  • sanitize_string (642-646)
  • normalize_string (651-657)
server/database.py (1)
  • get_device_stats (256-272)
🪛 Ruff (0.13.1)
front/plugins/_publisher_mqtt/mqtt.py

349-349: Unused function argument: userdata

(ARG001)


374-374: Unused function argument: mqtt_client

(ARG001)


374-374: Unused function argument: userdata

(ARG001)


374-374: Unused function argument: flags

(ARG001)


374-374: Unused function argument: properties

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: docker_dev

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af986aa and e3327d8.

📒 Files selected for processing (1)
  • front/plugins/_publisher_mqtt/mqtt.py (14 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
front/plugins/*/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

front/plugins/*/*.py: Plugin scripts must log only via mylog(), never using print()
Collect results with Plugin_Objects.add_object(...) during processing and call plugin_objects.write_result_file() exactly once at the end
Write results exclusively to /app/log/plugins/last_result..log using Plugin_Objects; do not write ad‑hoc result files
When creating/updating devices, define database_column_definitions and ensure watched fields map to notifications
Validate and normalize MAC addresses (use helper normalize_mac) before any DB write or object emission

Files:

  • front/plugins/_publisher_mqtt/mqtt.py
{server/**/*.py,front/plugins/*/*.py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

{server/**/*.py,front/plugins/*/*.py}: Never hardcode ports or secrets; define settings once (core ccd() or plugin manifest) and read via get_setting_value() everywhere
Use logger.mylog(level, [message]) with levels none/minimal/verbose/debug/trace; keep verbose output concise unless debugging
Reuse helpers in helper.py (timeNowTZ, normalize_mac, sanitizers) and prefer server/db/db_helper.py over raw SQL in new paths

Files:

  • front/plugins/_publisher_mqtt/mqtt.py
🧬 Code graph analysis (1)
front/plugins/_publisher_mqtt/mqtt.py (5)
server/plugin_utils.py (1)
  • getPluginObject (246-276)
front/plugins/plugin_helper.py (2)
  • Plugin_Objects (245-304)
  • write_result_file (288-291)
server/logger.py (3)
  • mylog (86-89)
  • Logger (55-93)
  • timeNowTZ (16-20)
server/helper.py (5)
  • timeNowTZ (35-45)
  • get_setting_value (358-411)
  • bytes_to_string (603-607)
  • sanitize_string (642-646)
  • normalize_string (651-657)
server/database.py (2)
  • get_device_stats (256-272)
  • open (35-77)
🪛 Ruff (0.13.1)
front/plugins/_publisher_mqtt/mqtt.py

350-350: Unused function argument: userdata

(ARG001)


375-375: Unused function argument: mqtt_client

(ARG001)


375-375: Unused function argument: userdata

(ARG001)


375-375: Unused function argument: flags

(ARG001)


375-375: Unused function argument: properties

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: docker_dev
🔇 Additional comments (1)
front/plugins/_publisher_mqtt/mqtt.py (1)

416-420: Don't mark the broker connection as established until on_connect fires

This is the same problem flagged in the earlier review: setting mqtt_connected_to_broker = True immediately after connect() means a failed handshake (wrong credentials/broker down) still flips the global to True. publish_mqtt then skips its guard, spins on mqtt_client.publish against a disconnected client, and never recovers cleanly. Let on_connect be the sole place that sets the flag to True once the broker actually acknowledges the session.

-    err_code = mqtt_client.connect(host=get_setting_value('MQTT_BROKER'), port=get_setting_value('MQTT_PORT'))
-    if (err_code == mqtt.MQTT_ERR_SUCCESS):
-        # We (prematurely) set the connection state to connected
-        # the callback may be delayed
-        mqtt_connected_to_broker = True
+    err_code = mqtt_client.connect(host=get_setting_value('MQTT_BROKER'), port=get_setting_value('MQTT_PORT'))
+    if err_code != mqtt.MQTT_ERR_SUCCESS:
+        mylog('minimal', [f"[{pluginName}] ❌ MQTT connect failed with rc={err_code}"])
+        mqtt_connected_to_broker = False
+        return None

Comment on lines 270 to 272
if not isinstance(message, str):
message = json.dumps(message).replace("'",'"')
message = json.dumps(message).replace("'", '"')

Copy link
Contributor

@coderabbitai coderabbitai bot Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

JSON payloads are being corrupted before publishing

json.dumps already produces valid JSON; replacing every ' with " will mangle legitimate apostrophes inside string values (e.g., O'Hara) into invalid JSON and change the data Home Assistant receives. Please drop the .replace("'", '"') so we send the canonical dump untouched.

-        message = json.dumps(message).replace("'", '"')
+        message = json.dumps(message)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not isinstance(message, str):
message = json.dumps(message).replace("'",'"')
message = json.dumps(message).replace("'", '"')
if not isinstance(message, str):
message = json.dumps(message)
🤖 Prompt for AI Agents
In front/plugins/_publisher_mqtt/mqtt.py around lines 270 to 272, the code uses
json.dumps(message).replace("'", '"') which corrupts valid JSON by replacing
apostrophes; remove the .replace("'", '"') and simply assign message =
json.dumps(message) when message is not a string so the canonical JSON produced
by json.dumps is sent unchanged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's working, so not intending to change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ingoratsdorf, understood! If it's working in your environment, no need to change it. Thanks for the clarification.

  (\   /)
 ( ._. )
o_("  ")("  ")

@jokob-sk
Copy link
Collaborator

Thanks for the PR @ingoratsdorf - LMK if good to merge

@ingoratsdorf
Copy link
Contributor Author

Working from my end. Good to merge. :-)

@jokob-sk jokob-sk merged commit 8485f6f into netalertx:main Sep 28, 2025
4 checks passed
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.

2 participants