Implement MQTT as a regular API ("api": "mqtt") instead of hardcoding it into the core (#550)#709
Implement MQTT as a regular API ("api": "mqtt") instead of hardcoding it into the core (#550)#709OKDaG wants to merge 2 commits into
Conversation
MQTT was added in volkszaehler#357 directly inside the vzlogger core (reading_thread), publishing readings on a separate path that bypassed aggregation and other channel logic (see volkszaehler#538). This makes MQTT a per-channel API selected via "api": "mqtt", so values flow through the channel buffer and the regular logging_thread -> api->send() path. As a result aggregation (aggmode/aggtime) and duplicate handling now apply to MQTT just like for the other APIs. - Add vz::api::MQTT (include/api/MQTT.hpp, src/api/MQTT.cpp): consumes the (already aggregated) channel buffer in send() like InfluxDB/Null and publishes via the shared broker connection; register_device() announces uuid/id. - Reduce MqttClient to a transport/connection layer: new thread-safe publish(topic, payload) plus getters for the global defaults; remove the old channel-based publish()/ChannelEntry/_chMap logic. - Remove the hardcoded mqttClient->publish() calls from reading_thread (the bypass) and register "mqtt" in the api factory in threads.cpp and MeterMap.cpp. - Add MQTT.cpp to the api library and the metermap mock build. - Document the change in etc/vzlogger.conf and add an "api": "mqtt" channel example. The global "mqtt" config block is kept for the shared broker connection (host/port/tls/credentials) and as defaults for topic/qos/retain/timestamp, which a channel may override. Note: this is a breaking change - configs that relied on the global mqtt block to auto-publish all channels must now set "api": "mqtt" per channel. Closes volkszaehler#550 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
thanks for picking up that ticket! i'm a bit wary of fully(?) generated code, two things i wonder:
also note that i can't test this as i'm not a user of mqtt. |
|
I used an AI assistant to do the heavy lifting not fully coding since my coding skills in c++ are sometimes "outdated", I reviewed every change and verified it end-to-end against a local Mosquitto broker About the Broker address in the mqtt section: I kept it there on purpose, because MQTT is a bit different from the HTTP-based APIs: it's a single persistent connection (one TCP socket + a background mosquitto_loop thread) that's naturally shared by all channels, whereas volkszaehler/influxdb are stateless per-request. Putting host/port/TLS/credentials per channel would mean either repeating them on every channel or opening one broker connection per channel. So the mqtt block stays as the shared connection, and per channel you only set "api": "mqtt" plus optional topic/qos/retain/timestamp overrides. If you'd prefer consistency with the other APIs, an alternative is to allow an optional per-channel host/port that falls back to the global block (pooling connections by host). Right now this is intentionally a clean break. The global block no longer auto-publishes every channel, you opt in per channel via "api": "mqtt". The one real regression is that a channel can only have one api, so the "send to volkszaehler and mirror to mqtt" use case isn't possible in a single channel anymore (you'd need a second channel with the same identifier/uuid). Options I see:
What's your preference? |
|
I applaud this change. Thank you for moving forward here. The situation was more than unsatisfying before. I'd probably want broker and topic with the channel though, there might be use cases where the readings from one channel to one broker and from another channel to another broker. In the program, the startup code would have to sift through the channel definitions, make a list of connections to build, bring up the connections and then take care that the correct channel publlishes to the correct connection. |
|
@OKDaG: all sounds good to me,
as you noted, a meter can have any number of channels, and those can attach to the same identifiers, the only thing i think we should have here is some testing by actual users of mqtt. |
Summary
Fixes #550. MQTT was added in #357 directly inside the vzlogger core
(
reading_thread), publishing on a separate path that bypassed aggregation andother channel logic (see #538).
This PR turns MQTT into a regular per-channel API, selected via
"api": "mqtt",exactly like
volkszaehler,influxdb, etc. Values now flow through the channelbuffer and the regular
logging_thread→api->send()path, so aggregation(
aggmode/aggtime) and duplicate handling now apply to MQTT as well.Changes
vz::api::MQTT(include/api/MQTT.hpp,src/api/MQTT.cpp): consumes the(already aggregated) channel buffer in
send()like InfluxDB/Null, honoringduplicates, and publishes via the shared broker connection.register_device()announces
uuid/id.MqttClientreduced to a transport/connection layer: new thread-safepublish(topic, payload)plus getters for the global defaults; the oldchannel-based
publish()/ChannelEntry/_chMaplogic is removed.mqttClient->publish()calls fromreading_thread(the bypass) and registered
"mqtt"in the API factory inthreads.cppandMeterMap.cpp.MQTT.cppto the api library and the metermap mock build.etc/vzlogger.conf+ added an"api": "mqtt"channelexample.
The global
"mqtt"config block is kept for the shared broker connection(host/port/tls/credentials) and as defaults for
topic/qos/retain/timestamp,which a channel may override.
Configs that relied on the global
mqttblock to auto-publish all channels mustnow set
"api": "mqtt"per channel. A channel can only have one API.Testing
ENABLE_MQTT=ON(Docker builder stage) compiles & links cleanly.build_test=on): 5/5 pass."api": "mqtt"publishesaggregated values to
…/agg(one peraggtime) plus theid/uuidannounce topics; with a non-mqtt api nothing is published (bypass gone).
🤖 Generated with Claude Code