dbus2mqtt 0.4.1__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dbus2mqtt might be problematic. Click here for more details.

Files changed (73) hide show
  1. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/release-drafter.yml +4 -5
  2. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.pre-commit-config.yaml +3 -3
  3. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/PKG-INFO +6 -4
  4. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/README.md +5 -3
  5. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docker/Dockerfile.dev +1 -1
  6. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docker/Dockerfile.pypi +1 -1
  7. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/dbus2mqtt_internal_state.yaml +7 -1
  8. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/home_assistant_media_player.md +1 -1
  9. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/home_assistant_media_player.yaml +4 -5
  10. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/linux_desktop.yaml +0 -4
  11. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples.md +1 -1
  12. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/flows.md +37 -11
  13. dbus2mqtt-0.4.2/renovate.json +36 -0
  14. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/config/__init__.py +8 -1
  15. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/dbus/dbus_client.py +1 -1
  16. dbus2mqtt-0.4.2/src/dbus2mqtt/flow/actions/log_action.py +36 -0
  17. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/flow/flow_processor.py +5 -1
  18. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/mqtt/mqtt_client.py +29 -8
  19. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/template/templating.py +11 -0
  20. dbus2mqtt-0.4.2/tests/flow/actions/test_log.py +28 -0
  21. dbus2mqtt-0.4.1/renovate.json +0 -22
  22. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.dockerignore +0 -0
  23. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.env.example +0 -0
  24. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  26. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/scripts/release-versions.py +0 -0
  27. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/workflows/ci.yml +0 -0
  28. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/workflows/docker-dev.yml +0 -0
  29. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/workflows/docker-stable.yml +0 -0
  30. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/workflows/pre-commit.yml +0 -0
  31. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/workflows/publish.yml +0 -0
  32. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.github/workflows/release-drafter.yml +0 -0
  33. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.gitignore +0 -0
  34. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.python-version +0 -0
  35. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.vscode/launch.json +0 -0
  36. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.vscode/settings.json +0 -0
  37. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/.yamllint.yml +0 -0
  38. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/LICENSE +0 -0
  39. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/debugging.md +0 -0
  40. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/bluez.md +0 -0
  41. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/bluez.yaml +0 -0
  42. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/dbus2mqtt_internal_state.md +0 -0
  43. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/docs/examples/linux_desktop.md +0 -0
  44. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/pyproject.toml +0 -0
  45. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/__init__.py +0 -0
  46. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/__main__.py +0 -0
  47. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/config/jsonarparse.py +0 -0
  48. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/dbus/dbus_types.py +0 -0
  49. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/dbus/dbus_util.py +0 -0
  50. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/dbus/introspection_patches/mpris_playerctl.py +0 -0
  51. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/dbus/introspection_patches/mpris_vlc.py +0 -0
  52. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/event_broker.py +0 -0
  53. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/flow/__init__.py +0 -0
  54. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/flow/actions/context_set.py +0 -0
  55. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/flow/actions/mqtt_publish.py +0 -0
  56. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/main.py +0 -0
  57. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/src/dbus2mqtt/template/dbus_template_functions.py +0 -0
  58. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/__init__.py +0 -0
  59. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/config/fixtures/payload_template_jinja_expressions.yaml +0 -0
  60. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/config/fixtures/payload_template_off.yaml +0 -0
  61. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/config/fixtures/schedule_cron_trigger.yaml +0 -0
  62. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/config/test_config.py +0 -0
  63. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/config/test_examples.py +0 -0
  64. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/conftest.py +0 -0
  65. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/dbus/test_dbus_client.py +0 -0
  66. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/dbus/test_dbus_client_mqtt_command.py +0 -0
  67. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/flow/actions/test_context_set.py +0 -0
  68. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/flow/actions/test_mqtt_publish.py +0 -0
  69. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/flow/test_flow_processor.py +0 -0
  70. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/flow/triggers/test_dbus_client_triggers.py +0 -0
  71. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/template/test_templating.py +0 -0
  72. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/tests/template/test_templating_config.py +0 -0
  73. {dbus2mqtt-0.4.1 → dbus2mqtt-0.4.2}/uv.lock +0 -0
@@ -1,7 +1,8 @@
1
- name-template: 'v$RESOLVED_VERSION 🌈'
1
+ name-template: 'v$RESOLVED_VERSION'
2
2
  tag-template: 'v$RESOLVED_VERSION'
3
3
  exclude-labels:
4
4
  - dependencies
5
+ - skip-changelog
5
6
  categories:
6
7
  - title: 🚨 Breaking changes
7
8
  labels:
@@ -13,13 +14,13 @@ categories:
13
14
  - title: 🐛 Bug Fixes
14
15
  labels:
15
16
  - bugfix
16
- - title: 🧰 Maintenance
17
+ - title: 🧰 Misc
17
18
  labels:
18
19
  - chore
19
20
  - documentation
20
21
  - title: ⬆️ Dependency updates
21
22
  labels:
22
- - "dependencies"
23
+ - dependencies
23
24
  exclude-contributors:
24
25
  - jwnmulder
25
26
  autolabeler:
@@ -47,6 +48,4 @@ version-resolver:
47
48
  default: patch
48
49
  change-template: '* $TITLE (#$NUMBER)'
49
50
  template: |
50
- ## Changes
51
-
52
51
  $CHANGES
@@ -33,7 +33,7 @@ repos:
33
33
  - --strict
34
34
 
35
35
  - repo: https://github.com/astral-sh/ruff-pre-commit
36
- rev: v0.12.1
36
+ rev: v0.12.2
37
37
  hooks:
38
38
  - id: ruff
39
39
  args:
@@ -42,11 +42,11 @@ repos:
42
42
  - I
43
43
 
44
44
  - repo: https://github.com/astral-sh/uv-pre-commit
45
- rev: 0.7.16
45
+ rev: 0.7.19
46
46
  hooks:
47
47
  - id: uv-lock
48
48
 
49
49
  - repo: https://github.com/RobertCraigie/pyright-python
50
- rev: v1.1.402
50
+ rev: v1.1.403
51
51
  hooks:
52
52
  - id: pyright
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbus2mqtt
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: A Python tool to expose Linux D-Bus signals, methods and properties over MQTT - featuring templating, payload enrichment and Home Assistant-ready examples
5
5
  Project-URL: Repository, https://github.com/jwnmulder/dbus2mqtt.git
6
6
  Project-URL: Issues, https://github.com/jwnmulder/dbus2mqtt/issues
@@ -32,8 +32,8 @@ Description-Content-Type: text/markdown
32
32
 
33
33
  # dbus2mqtt
34
34
 
35
- **dbus2mqtt** is a Python application that bridges **Linux D-Bus** with **MQTT**.
36
- It lets you forward D-Bus signals and properties to MQTT topics, call D-Bus methods via MQTT messages, and shape payloads using flexible **Jinja2 templating**.
35
+ **dbus2mqtt** is a Python application that bridges **DBus** with **MQTT**.
36
+ It lets you forward Linux D-Bus signals and properties to MQTT topics, call D-Bus methods via MQTT messages, and shape payloads using flexible **Jinja2 templating**.
37
37
 
38
38
  This makes it easy to integrate Linux desktop services or system signals into MQTT-based workflows - including **Home Assistant**.
39
39
 
@@ -154,6 +154,8 @@ or
154
154
  mqtt:
155
155
  host: localhost
156
156
  port: 1883
157
+ subscription_topics:
158
+ - dbus2mqtt/#
157
159
  ```
158
160
 
159
161
  ### Exposing dbus methods
@@ -224,7 +226,7 @@ dbus:
224
226
 
225
227
  ## Flows
226
228
 
227
- TODO: Document flows, for now see the [MPRIS to Home Assistant Media Player integration](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md) example
229
+ A reference of all supported flow triggers and actions can be found on [Flows](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/flows.md)
228
230
 
229
231
  ## Jinja templating
230
232
 
@@ -1,7 +1,7 @@
1
1
  # dbus2mqtt
2
2
 
3
- **dbus2mqtt** is a Python application that bridges **Linux D-Bus** with **MQTT**.
4
- It lets you forward D-Bus signals and properties to MQTT topics, call D-Bus methods via MQTT messages, and shape payloads using flexible **Jinja2 templating**.
3
+ **dbus2mqtt** is a Python application that bridges **DBus** with **MQTT**.
4
+ It lets you forward Linux D-Bus signals and properties to MQTT topics, call D-Bus methods via MQTT messages, and shape payloads using flexible **Jinja2 templating**.
5
5
 
6
6
  This makes it easy to integrate Linux desktop services or system signals into MQTT-based workflows - including **Home Assistant**.
7
7
 
@@ -122,6 +122,8 @@ or
122
122
  mqtt:
123
123
  host: localhost
124
124
  port: 1883
125
+ subscription_topics:
126
+ - dbus2mqtt/#
125
127
  ```
126
128
 
127
129
  ### Exposing dbus methods
@@ -192,7 +194,7 @@ dbus:
192
194
 
193
195
  ## Flows
194
196
 
195
- TODO: Document flows, for now see the [MPRIS to Home Assistant Media Player integration](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md) example
197
+ A reference of all supported flow triggers and actions can be found on [Flows](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/flows.md)
196
198
 
197
199
  ## Jinja templating
198
200
 
@@ -25,7 +25,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
25
25
  uv sync --frozen --no-dev
26
26
 
27
27
  # Then, use a final image without uv
28
- FROM python:3.13-slim-bookworm
28
+ FROM python:3.13-slim-bookworm@sha256:6544e0e002b40ae0f59bc3618b07c1e48064c4faed3a15ae2fbd2e8f663e8283
29
29
  # It is important to use the image that matches the builder, as the path to the
30
30
  # Python executable must be the same, e.g., using `python:3.12-slim-bookworm`
31
31
  # will fail.
@@ -1,4 +1,4 @@
1
- FROM python:3.13-slim-bookworm
1
+ FROM python:3.13-slim-bookworm@sha256:6544e0e002b40ae0f59bc3618b07c1e48064c4faed3a15ae2fbd2e8f663e8283
2
2
 
3
3
  WORKDIR /app
4
4
 
@@ -1,6 +1,8 @@
1
1
  # mqtt:
2
2
  # host:
3
3
  # port:
4
+ # subscription_topics:
5
+ # - dbus2mqtt/#
4
6
 
5
7
  dbus:
6
8
  subscriptions:
@@ -37,4 +39,8 @@ flows:
37
39
  payload_type: json
38
40
  payload_template:
39
41
  now: "{{ now().isoformat() }}"
40
- dbus_list: "{{ dbus_list('*') }}"
42
+ dbus_list_res: "{{ dbus_list('*') }}"
43
+ - type: log
44
+ level: INFO
45
+ msg: >
46
+ Just published: dbus_list={{ dbus_list('*') }} to topic 'dbus2mqtt/state'
@@ -12,7 +12,7 @@ Pre-requisites:
12
12
  Features:
13
13
 
14
14
  * dbus subscription using `org.mpris.MediaPlayer2.*` wildcard to support multiple concurrent MRPIS players
15
- * Every 5 seconds, the state if the `first` known MPRIS player is published to MQTT topic `dbus2mqtt/org.mpris.MediaPlayer2/state`
15
+ * Every 5 seconds, the state of the `first` known MPRIS player is published to MQTT topic `dbus2mqtt/org.mpris.MediaPlayer2/state`
16
16
  * Every MPRIS property update immediately publishes the state to MQTT topic `dbus2mqtt/org.mpris.MediaPlayer2/state`
17
17
  * Support for player commands (see below)
18
18
 
@@ -1,7 +1,3 @@
1
- # mqtt:
2
- # host:
3
- # port:
4
-
5
1
  dbus:
6
2
  subscriptions:
7
3
 
@@ -68,10 +64,13 @@ dbus:
68
64
  topic: dbus2mqtt/org.mpris.MediaPlayer2/state
69
65
  payload_type: json
70
66
  payload_template: |
67
+ {% set metadata = player_properties.get('Metadata') or {} %}
68
+ {% set metadata_xesam_url = metadata.get('xesam:url', '') | urldecode %}
71
69
  {{
72
70
  { 'bus_name': mpris_bus_name }
73
71
  | combine(player_properties)
74
- | combine ({ 'Position': seeked_position } if seeked_position else {})
72
+ | combine({ 'Position': seeked_position } if seeked_position else {})
73
+ | combine({ 'Metadata': { 'xesam:url': metadata_xesam_url } }, recursive=True)
75
74
  }}
76
75
 
77
76
  - name: "publish local art image"
@@ -1,7 +1,3 @@
1
- # mqtt:
2
- # host:
3
- # port:
4
-
5
1
  dbus:
6
2
  subscriptions:
7
3
 
@@ -1,5 +1,5 @@
1
1
  Examples in this repository
2
2
 
3
3
  * [MPRIS to Home Assistant Media Player integration](examples/home_assistant_media_player.md)
4
- * [bluez](examples/bluez.md)
4
+ * [BlueZ](examples/bluez.md)
5
5
  * [Publish dbus2mqtt internal state](examples/dbus2mqtt_internal_state.md)
@@ -1,6 +1,6 @@
1
1
  # Flows
2
2
 
3
- **dbus2mqtt** allows you to add additional processing logic (flows) for when events occur. Configuration is best done in yaml and a complete example can be found here: [MPRIS to Home Assistant Media Player integration](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md)
3
+ **dbus2mqtt** allows you to add additional processing logic (flows) for when events occur. Configuration is done in yaml and a complete example can be found in [home_assistant_media_player.yaml](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.yaml) which is part of the [MPRIS to Home Assistant Media Player integration](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md) example
4
4
 
5
5
  Flows can be defined on a global or dbus subscription level and can be triggered by any of the following events:
6
6
 
@@ -9,12 +9,26 @@ Flows can be defined on a global or dbus subscription level and can be triggered
9
9
  * `object_added` when a new bus_name is registered on dbus
10
10
  * `object_removed` when a bus_name is removed from dbus
11
11
 
12
- Within each flow a set of actions can be configured. These are executed in order
12
+ Within each flow a set of actions can be configured. These are executed in the order as defined in yaml
13
13
 
14
+ * `log` for logging message
14
15
  * `context_set` to set variables
15
16
  * `mqtt_publish` to publish a mqtt message
16
17
 
17
- Actions support string templating which is based on jinja2
18
+ An example
19
+
20
+ ```yaml
21
+ flows:
22
+ - name: "Example flow"
23
+ triggers:
24
+ - type: schedule
25
+ interval: {seconds: 5}
26
+ actions:
27
+ - type: log
28
+ msg: hello from example flow
29
+ ```
30
+
31
+ Some action parameters allow the use of jinja2 templating. dbus2mqtt supports both builtin jinja2 filters and comes with additional filters from [jinja2-ansible-filters](https://pypi.org/project/jinja2-ansible-filters/). When supported, it is documented below.
18
32
 
19
33
  ## Flow triggers
20
34
 
@@ -22,7 +36,6 @@ Actions support string templating which is based on jinja2
22
36
 
23
37
  ```yaml
24
38
  type: schedule
25
- cron: {second: 5}
26
39
  interval: {seconds: 5}
27
40
  ```
28
41
 
@@ -41,6 +54,12 @@ When triggered, the following context parameters are available
41
54
 
42
55
  ### dbus_signal
43
56
 
57
+ ```yaml
58
+ type: dbus_signal
59
+ interface: org.freedesktop.DBus.Properties
60
+ signal: PropertiesChanged
61
+ ```
62
+
44
63
  DBus signals triggers must be configured with an anterface and path. Note that only subscribed signals can be configured as a trigger.
45
64
 
46
65
  | key | description |
@@ -86,6 +105,19 @@ When triggered, the following context parameters are available
86
105
 
87
106
  ## Flow actions
88
107
 
108
+ ### log
109
+
110
+ ```yaml
111
+ type: log
112
+ msg: your log message
113
+ levvel: INFO
114
+ ```
115
+
116
+ | key | type | description |
117
+ |------------------|------------------|--------------|
118
+ | msg | str | a templated string |
119
+ | level | str | One of ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], defaults to 'INFO' |
120
+
89
121
  ### context_set
90
122
 
91
123
  ```yaml
@@ -101,8 +133,6 @@ global_context: {}
101
133
  | dbus_object_context | dict | Per dbus object context, shared between multiple flow executions. Value can be a dict of strings or dict of templated strings |
102
134
  | global_context | dict | Global context, shared between multiple flow executions, over all subscriptions. Value can be a dict of strings or dict of templated strings |
103
135
 
104
-
105
-
106
136
  ### mqtt_publish
107
137
 
108
138
  ```yaml
@@ -113,11 +143,7 @@ payload_template: {PlaybackStatus: "Off"}
113
143
  ```
114
144
 
115
145
  | key | type | description |
116
- |------------------|------------------|-------------|
146
+ |------------------|------------------|--------------|
117
147
  | topic | string | mqtt topic the messaage is published to |
118
148
  | payload_type | string | any of [json, yaml, text], defaults to json, format the message is published in to mqtt |
119
149
  | payload_template | string, dict | value can be a string, a dict of strings, a templated string or a nested dict of templated strings |
120
-
121
- ## Jinja2 based templating
122
-
123
- Some configuration values allow the use of jinja 2 templating. dbus2mqtt supports both the builtin filters and comes with additional filters from [jinja2-ansible-filters](https://pypi.org/project/jinja2-ansible-filters/)
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": ["local>jwnmulder/renovate-config"],
4
+
5
+ "labels": ["dependencies"],
6
+
7
+ "packageRules": [
8
+ {
9
+ "matchJsonata": ["isBreaking"],
10
+ "dependencyDashboardApproval": true
11
+ },
12
+ {
13
+ "matchFileNames": [".pre-commit-config.yaml"],
14
+ "matchUpdateTypes": ["minor", "patch"],
15
+ "automerge": true,
16
+ "addLabels": ["automerge"]
17
+ },
18
+ {
19
+ "description": "Disable Renovate for .python-version",
20
+ "matchFileNames": [".python-version"],
21
+ "enabled": false
22
+ },
23
+ {
24
+ "matchDatasources": ["docker"],
25
+ "matchPackageNames": ["python"],
26
+ "pinDigests": true,
27
+ "separateMinorPatch": true
28
+ },
29
+ {
30
+ "matchDatasources": ["docker"],
31
+ "matchPackageNames": "python",
32
+ "matchUpdateTypes": ["patch", "pin", "digest"],
33
+ "extends": ["schedule:weekly"]
34
+ }
35
+ ]
36
+ }
@@ -107,8 +107,14 @@ class FlowActionMqttPublishConfig:
107
107
  type: Literal["mqtt_publish"] = "mqtt_publish"
108
108
  payload_type: Literal["json", "yaml", "text", "binary"] = "json"
109
109
 
110
+ @dataclass
111
+ class FlowActionLogConfig:
112
+ msg: str
113
+ type: Literal["log"] = "log"
114
+ level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"
115
+
110
116
  FlowActionConfig = Annotated[
111
- FlowActionMqttPublishConfig | FlowActionContextSetConfig,
117
+ FlowActionMqttPublishConfig | FlowActionContextSetConfig | FlowActionLogConfig,
112
118
  Field(discriminator="type")
113
119
  ]
114
120
 
@@ -157,6 +163,7 @@ class MqttConfig:
157
163
  username: str
158
164
  password: SecretStr
159
165
  port: int = 1883
166
+ subscription_topics: list[str] = field(default_factory=lambda: ['dbus2mqtt/#'])
160
167
 
161
168
  @dataclass
162
169
  class Config:
@@ -512,7 +512,7 @@ class DbusClient:
512
512
  # clean lingering interface messgage handler from bus
513
513
  self.bus.remove_message_handler(proxy_interface._message_handler)
514
514
 
515
- # For now that InterfacesRemoved signal means the entire object is removed form D-Bus
515
+ # For now that InterfacesRemoved signal means the entire object is removed from D-Bus
516
516
  del self.subscriptions[bus_name].path_objects[path]
517
517
 
518
518
  # cleanup the entire BusNameSubscriptions if no more objects are subscribed
@@ -0,0 +1,36 @@
1
+
2
+ import logging
3
+
4
+ from jinja2.exceptions import TemplateError
5
+
6
+ from dbus2mqtt import AppContext
7
+ from dbus2mqtt.config import FlowActionLogConfig
8
+ from dbus2mqtt.flow import FlowAction, FlowExecutionContext
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class LogAction(FlowAction):
13
+
14
+ def __init__(self, config: FlowActionLogConfig, app_context: AppContext):
15
+ self.config = config
16
+ self.templating = app_context.templating
17
+
18
+ async def execute(self, context: FlowExecutionContext):
19
+
20
+ render_context = context.get_aggregated_context()
21
+
22
+ log_msg = self.config.msg
23
+ log_level = logging._nameToLevel.get(self.config.level.upper(), logging.INFO)
24
+
25
+ try:
26
+ log_msg = await self.templating.async_render_template(
27
+ templatable=self.config.msg,
28
+ context=render_context,
29
+ res_type=str
30
+ )
31
+
32
+ except TemplateError as e:
33
+ logger.warning(f"Error rendering jinja template, flow: '{context.name or ''}', msg={e}, msg={self.config.msg}, render_context={render_context}", exc_info=True)
34
+ return
35
+
36
+ logger.log(level=log_level, msg=log_msg)
@@ -17,6 +17,7 @@ from dbus2mqtt.config import (
17
17
  from dbus2mqtt.event_broker import FlowTriggerMessage
18
18
  from dbus2mqtt.flow import FlowAction, FlowExecutionContext
19
19
  from dbus2mqtt.flow.actions.context_set import ContextSetAction
20
+ from dbus2mqtt.flow.actions.log_action import LogAction
20
21
  from dbus2mqtt.flow.actions.mqtt_publish import MqttPublishAction
21
22
 
22
23
  logger = logging.getLogger(__name__)
@@ -102,8 +103,11 @@ class FlowActionContext:
102
103
  action = None
103
104
  if action_config.type == "context_set":
104
105
  action = ContextSetAction(action_config, self.app_context)
105
- if action_config.type == "mqtt_publish":
106
+ elif action_config.type == "mqtt_publish":
106
107
  action = MqttPublishAction(action_config, self.app_context)
108
+ elif action_config.type == "log":
109
+ action = LogAction(action_config, self.app_context)
110
+
107
111
  if action:
108
112
  res.append(action)
109
113
 
@@ -13,6 +13,8 @@ import paho.mqtt.client as mqtt
13
13
  import yaml
14
14
 
15
15
  from paho.mqtt.enums import CallbackAPIVersion
16
+ from paho.mqtt.packettypes import PacketTypes
17
+ from paho.mqtt.properties import Properties
16
18
  from paho.mqtt.subscribeoptions import SubscribeOptions
17
19
 
18
20
  from dbus2mqtt import AppContext
@@ -27,8 +29,11 @@ class MqttClient:
27
29
  self.event_broker = app_context.event_broker
28
30
 
29
31
  unique_client_id_postfix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
32
+ self.client_id_prefix = "dbus2mqtt-"
33
+ self.client_id = f"{self.client_id_prefix}{unique_client_id_postfix}"
34
+
30
35
  self.client = mqtt.Client(
31
- client_id=f"dbus2mqtt-client-{unique_client_id_postfix}",
36
+ client_id=self.client_id,
32
37
  protocol=mqtt.MQTTv5,
33
38
  callback_api_version=CallbackAPIVersion.VERSION2
34
39
  )
@@ -84,7 +89,16 @@ class MqttClient:
84
89
  if first_message:
85
90
  await asyncio.wait_for(self.connected_event.wait(), timeout=5)
86
91
 
87
- self.client.publish(topic=msg.topic, payload=payload or "").wait_for_publish(timeout=1000)
92
+ publish_properties = Properties(PacketTypes.PUBLISH)
93
+ publish_properties.UserProperty = ("client_id", self.client_id)
94
+
95
+ publish_info = self.client.publish(
96
+ topic=msg.topic,
97
+ payload=payload or "",
98
+ properties=publish_properties
99
+ )
100
+ publish_info.wait_for_publish(timeout=1000)
101
+
88
102
  if first_message:
89
103
  logger.info(f"First message published: topic={msg.topic}, payload={payload_log_msg}")
90
104
  first_message = False
@@ -100,22 +114,29 @@ class MqttClient:
100
114
  logger.warning(f"on_connect: Failed to connect: {reason_code}. Will retry connection")
101
115
  else:
102
116
  logger.info(f"on_connect: Connected to {self.config.host}:{self.config.port}")
103
- # Subscribing in on_connect() means that if we lose the connection and
104
- # reconnect then subscriptions will be renewed.
105
- # TODO: Determine topics based on config
106
- client.subscribe("dbus2mqtt/#", options=SubscribeOptions(noLocal=True))
117
+
118
+ subscriptions = [(t, SubscribeOptions(noLocal=True)) for t in self.config.subscription_topics]
119
+ client.subscribe(subscriptions)
107
120
 
108
121
  self.loop.call_soon_threadsafe(self.connected_event.set)
109
122
 
110
123
  def on_message(self, client: mqtt.Client, userdata: Any, msg: mqtt.MQTTMessage):
111
124
 
112
- # TODO: Skip messages being sent by other dbus2mqtt clients
113
-
125
+ # Skip retained messages
114
126
  payload = msg.payload.decode()
115
127
  if msg.retain:
116
128
  logger.info(f"on_message: skipping msg with retain=True, topic={msg.topic}, payload={payload}")
117
129
  return
118
130
 
131
+ # Skip messages being sent by other dbus2mqtt clients
132
+ if msg.properties:
133
+ user_properties: list[tuple[str, object]] = getattr(msg.properties, "UserProperty", [])
134
+ client_id = next((str(v) for k, v in user_properties if k == "client_id"), None)
135
+ if client_id and client_id != self.client_id:
136
+ logger.info(f"on_message: skipping msg from another dbus2mqtt client, topic={msg.topic}, client_id={client_id}")
137
+ if client_id and client_id.startswith(self.client_id_prefix):
138
+ return
139
+
119
140
  try:
120
141
  json_payload = json.loads(payload) if payload else {}
121
142
  logger.debug(f"on_message: msg.topic={msg.topic}, msg.payload={json.dumps(json_payload)}")
@@ -1,3 +1,4 @@
1
+ import urllib.parse
1
2
 
2
3
  from datetime import datetime
3
4
  from typing import Any, TypeVar
@@ -7,11 +8,18 @@ from jinja2.nativetypes import NativeEnvironment
7
8
 
8
9
  TemplateResultType = TypeVar('TemplateResultType')
9
10
 
11
+ def urldecode(string):
12
+ return urllib.parse.unquote(string)
13
+
10
14
  class TemplateEngine:
11
15
  def __init__(self):
12
16
 
13
17
  engine_globals = {}
14
18
  engine_globals['now'] = datetime.now
19
+ engine_globals['urldecode'] = urldecode
20
+
21
+ engine_filters = {}
22
+ engine_filters['urldecode'] = urldecode
15
23
 
16
24
  self.jinja2_env = NativeEnvironment(
17
25
  loader=BaseLoader(),
@@ -32,6 +40,9 @@ class TemplateEngine:
32
40
  self.jinja2_env.globals.update(engine_globals)
33
41
  self.jinja2_async_env.globals.update(engine_globals)
34
42
 
43
+ self.jinja2_env.filters.update(engine_filters)
44
+ self.jinja2_async_env.filters.update(engine_filters)
45
+
35
46
  def add_functions(self, custom_functions: dict[str, Any]):
36
47
  self.jinja2_env.globals.update(custom_functions)
37
48
  self.jinja2_async_env.globals.update(custom_functions)
@@ -0,0 +1,28 @@
1
+ from datetime import datetime
2
+
3
+ import pytest
4
+
5
+ from dbus2mqtt.config import (
6
+ FlowActionLogConfig,
7
+ FlowTriggerScheduleConfig,
8
+ )
9
+ from dbus2mqtt.flow.flow_processor import FlowTriggerMessage
10
+ from tests import mocked_app_context, mocked_flow_processor
11
+
12
+
13
+ @pytest.mark.asyncio
14
+ async def test_context():
15
+
16
+ app_context = mocked_app_context()
17
+
18
+ trigger_config = FlowTriggerScheduleConfig()
19
+ processor, flow_config = mocked_flow_processor(app_context, trigger_config, actions=[
20
+ FlowActionLogConfig(
21
+ msg="{{ 'templated-test-str' }}",
22
+ level="INFO"
23
+ )
24
+ ])
25
+
26
+ await processor._process_flow_trigger(
27
+ FlowTriggerMessage(flow_config, trigger_config, datetime.now())
28
+ )
@@ -1,22 +0,0 @@
1
- {
2
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": [
4
- "local>jwnmulder/renovate-config"
5
- ],
6
-
7
- "labels": ["dependencies"],
8
-
9
- "packageRules": [
10
- {
11
- "matchFileNames": [".pre-commit-config.yaml"],
12
- "matchUpdateTypes": ["minor", "patch"],
13
- "automerge": true,
14
- "addLabels": ["automerge"]
15
- },
16
- {
17
- "description": "Disable Renovate for .python-version",
18
- "matchFileNames": [".python-version"],
19
- "enabled": false
20
- }
21
- ]
22
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes