dbus2mqtt 0.3.1__tar.gz → 0.4.1__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.3.1 → dbus2mqtt-0.4.1}/.github/release-drafter.yml +2 -2
  2. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/workflows/docker-stable.yml +2 -0
  3. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.pre-commit-config.yaml +3 -3
  4. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.vscode/launch.json +8 -0
  5. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/PKG-INFO +18 -8
  6. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/README.md +17 -7
  7. dbus2mqtt-0.4.1/docs/examples/bluez.md +37 -0
  8. dbus2mqtt-0.4.1/docs/examples/bluez.yaml +86 -0
  9. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/examples/dbus2mqtt_internal_state.yaml +1 -1
  10. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/examples/home_assistant_media_player.md +9 -2
  11. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/examples/home_assistant_media_player.yaml +7 -3
  12. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/examples/linux_desktop.md +1 -1
  13. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/examples/linux_desktop.yaml +1 -1
  14. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/examples.md +1 -0
  15. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/flows.md +10 -9
  16. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/pyproject.toml +4 -2
  17. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/config/__init__.py +24 -5
  18. dbus2mqtt-0.4.1/src/dbus2mqtt/dbus/dbus_client.py +820 -0
  19. dbus2mqtt-0.4.1/src/dbus2mqtt/dbus/dbus_types.py +37 -0
  20. dbus2mqtt-0.4.1/src/dbus2mqtt/dbus/dbus_util.py +24 -0
  21. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/event_broker.py +1 -21
  22. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/flow/flow_processor.py +19 -5
  23. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/main.py +17 -3
  24. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/mqtt/mqtt_client.py +10 -3
  25. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/template/dbus_template_functions.py +2 -2
  26. dbus2mqtt-0.4.1/tests/conftest.py +16 -0
  27. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/dbus/test_dbus_client.py +7 -5
  28. dbus2mqtt-0.4.1/tests/dbus/test_dbus_client_mqtt_command.py +206 -0
  29. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/flow/test_flow_processor.py +42 -0
  30. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/flow/triggers/test_dbus_client_triggers.py +86 -4
  31. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/uv.lock +4 -4
  32. dbus2mqtt-0.3.1/src/dbus2mqtt/dbus/dbus_client.py +0 -476
  33. dbus2mqtt-0.3.1/src/dbus2mqtt/dbus/dbus_types.py +0 -23
  34. dbus2mqtt-0.3.1/src/dbus2mqtt/dbus/dbus_util.py +0 -23
  35. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.dockerignore +0 -0
  36. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.env.example +0 -0
  37. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  38. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  39. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/scripts/release-versions.py +0 -0
  40. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/workflows/ci.yml +0 -0
  41. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/workflows/docker-dev.yml +0 -0
  42. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/workflows/pre-commit.yml +0 -0
  43. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/workflows/publish.yml +0 -0
  44. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.github/workflows/release-drafter.yml +0 -0
  45. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.gitignore +0 -0
  46. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.python-version +0 -0
  47. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.vscode/settings.json +0 -0
  48. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/.yamllint.yml +0 -0
  49. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/LICENSE +0 -0
  50. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docker/Dockerfile.dev +0 -0
  51. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docker/Dockerfile.pypi +0 -0
  52. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/debugging.md +0 -0
  53. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/docs/examples/dbus2mqtt_internal_state.md +0 -0
  54. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/renovate.json +0 -0
  55. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/__init__.py +0 -0
  56. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/__main__.py +0 -0
  57. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/config/jsonarparse.py +0 -0
  58. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/dbus/introspection_patches/mpris_playerctl.py +0 -0
  59. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/dbus/introspection_patches/mpris_vlc.py +0 -0
  60. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/flow/__init__.py +0 -0
  61. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/flow/actions/context_set.py +0 -0
  62. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/flow/actions/mqtt_publish.py +0 -0
  63. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/src/dbus2mqtt/template/templating.py +0 -0
  64. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/__init__.py +0 -0
  65. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/config/fixtures/payload_template_jinja_expressions.yaml +0 -0
  66. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/config/fixtures/payload_template_off.yaml +0 -0
  67. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/config/fixtures/schedule_cron_trigger.yaml +0 -0
  68. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/config/test_config.py +0 -0
  69. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/config/test_examples.py +0 -0
  70. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/flow/actions/test_context_set.py +0 -0
  71. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/flow/actions/test_mqtt_publish.py +0 -0
  72. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/template/test_templating.py +0 -0
  73. {dbus2mqtt-0.3.1 → dbus2mqtt-0.4.1}/tests/template/test_templating_config.py +0 -0
@@ -38,12 +38,12 @@ version-resolver:
38
38
  minor:
39
39
  labels:
40
40
  - minor
41
- - feature
42
- - enhancement
43
41
  - breaking-change # minor until we are on 1.x
44
42
  patch:
45
43
  labels:
46
44
  - patch
45
+ - feature # patch until we are on 1.x
46
+ - enhancement # patch until we are on 1.x
47
47
  default: patch
48
48
  change-template: '* $TITLE (#$NUMBER)'
49
49
  template: |
@@ -63,6 +63,8 @@ jobs:
63
63
  # type=ref,event=branch
64
64
  # type=ref,event=pr
65
65
  # type=semver,pattern={{version}},value=${{ matrix.version }}
66
+ flavor:
67
+ latest=false
66
68
  labels: |
67
69
  org.opencontainers.image.source=https://github.com/jwnmulder/dbus2mqtt
68
70
 
@@ -33,7 +33,7 @@ repos:
33
33
  - --strict
34
34
 
35
35
  - repo: https://github.com/astral-sh/ruff-pre-commit
36
- rev: v0.11.9
36
+ rev: v0.12.1
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.3
45
+ rev: 0.7.16
46
46
  hooks:
47
47
  - id: uv-lock
48
48
 
49
49
  - repo: https://github.com/RobertCraigie/pyright-python
50
- rev: v1.1.400
50
+ rev: v1.1.402
51
51
  hooks:
52
52
  - id: pyright
@@ -8,6 +8,14 @@
8
8
  "module": "dbus2mqtt",
9
9
  "console": "integratedTerminal",
10
10
  "args": "--config docs/examples/home_assistant_media_player.yaml"
11
+ },
12
+ {
13
+ "name": "dbus2mqtt - bluez",
14
+ "type": "debugpy",
15
+ "request": "launch",
16
+ "module": "dbus2mqtt",
17
+ "console": "integratedTerminal",
18
+ "args": "--config docs/examples/bluez.yaml"
11
19
  }
12
20
  ]
13
21
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbus2mqtt
3
- Version: 0.3.1
3
+ Version: 0.4.1
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
@@ -53,7 +53,7 @@ Initial testing has focused on MPRIS integration. A table of tested MPRIS player
53
53
 
54
54
  ## Getting started with dbus2mqtt
55
55
 
56
- Create a `config.yaml` file with the contents shown below. This configuration will expose all bus properties from the `org.mpris.MediaPlayer2.Player` interface to MQTT on the `dbus2mqtt/org.mpris.MediaPlayer2/state` topic. Have a look at [docs/examples](docs/examples.md) for more examples
56
+ Create a `config.yaml` file with the contents shown below. This configuration will expose all bus properties from the `org.mpris.MediaPlayer2.Player` interface to MQTT on the `dbus2mqtt/org.mpris.MediaPlayer2/state` topic. Have a look at [docs/examples](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples.md) for more examples
57
57
 
58
58
  ```yaml
59
59
  dbus:
@@ -68,7 +68,7 @@ dbus:
68
68
  flows:
69
69
  - name: "Publish MPRIS state"
70
70
  triggers:
71
- - type: bus_name_added
71
+ - type: object_added
72
72
  - type: schedule
73
73
  interval: {seconds: 5}
74
74
  actions:
@@ -111,8 +111,8 @@ cp docs/examples/home_assistant_media_player.yaml $HOME/.config/dbus2mqtt/config
111
111
  cp .env.example $HOME/.config/dbus2mqtt/.env
112
112
 
113
113
  # run image and automatically start on reboot
114
- docker pull jwnmulder/dbus2mqtt
115
- docker run --detach --name dbus2mqtt \
114
+ sudo docker pull jwnmulder/dbus2mqtt
115
+ sudo docker run --detach --name dbus2mqtt \
116
116
  --volume "$HOME"/.config/dbus2mqtt:"$HOME"/.config/dbus2mqtt \
117
117
  --volume /run/user:/run/user \
118
118
  --env DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
@@ -173,21 +173,31 @@ dbus:
173
173
 
174
174
  This configuration will expose 2 methods. Triggering methods can be done by publishing json messages to the `dbus2mqtt/org.mpris.MediaPlayer2/command` MQTT topic. Arguments can be passed along in `args`.
175
175
 
176
- Note that methods are called on **all** bus_names matching the configured pattern
176
+ Some examples that call methods on **all** bus_names matching the configured pattern
177
177
 
178
178
  ```json
179
179
  {
180
- "method" : "Play",
180
+ "method": "Play",
181
181
  }
182
182
  ```
183
183
 
184
184
  ```json
185
185
  {
186
- "method" : "OpenUri",
186
+ "method": "OpenUri",
187
187
  "args": []
188
188
  }
189
189
  ```
190
190
 
191
+ To specifically target objects the properties `bus_name` and/or `path` can be used. Both properties support wildcards
192
+
193
+ ```json
194
+ {
195
+ "method": "Play",
196
+ "bus_name": "*.firefox",
197
+ "path": "/org/mpris/MediaPlayer2"
198
+ }
199
+ ```
200
+
191
201
  ### Exposing dbus signals
192
202
 
193
203
  Publishing signals to MQTT topics works by subscribing to the relevant signal and using flows for publishing
@@ -21,7 +21,7 @@ Initial testing has focused on MPRIS integration. A table of tested MPRIS player
21
21
 
22
22
  ## Getting started with dbus2mqtt
23
23
 
24
- Create a `config.yaml` file with the contents shown below. This configuration will expose all bus properties from the `org.mpris.MediaPlayer2.Player` interface to MQTT on the `dbus2mqtt/org.mpris.MediaPlayer2/state` topic. Have a look at [docs/examples](docs/examples.md) for more examples
24
+ Create a `config.yaml` file with the contents shown below. This configuration will expose all bus properties from the `org.mpris.MediaPlayer2.Player` interface to MQTT on the `dbus2mqtt/org.mpris.MediaPlayer2/state` topic. Have a look at [docs/examples](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples.md) for more examples
25
25
 
26
26
  ```yaml
27
27
  dbus:
@@ -36,7 +36,7 @@ dbus:
36
36
  flows:
37
37
  - name: "Publish MPRIS state"
38
38
  triggers:
39
- - type: bus_name_added
39
+ - type: object_added
40
40
  - type: schedule
41
41
  interval: {seconds: 5}
42
42
  actions:
@@ -79,8 +79,8 @@ cp docs/examples/home_assistant_media_player.yaml $HOME/.config/dbus2mqtt/config
79
79
  cp .env.example $HOME/.config/dbus2mqtt/.env
80
80
 
81
81
  # run image and automatically start on reboot
82
- docker pull jwnmulder/dbus2mqtt
83
- docker run --detach --name dbus2mqtt \
82
+ sudo docker pull jwnmulder/dbus2mqtt
83
+ sudo docker run --detach --name dbus2mqtt \
84
84
  --volume "$HOME"/.config/dbus2mqtt:"$HOME"/.config/dbus2mqtt \
85
85
  --volume /run/user:/run/user \
86
86
  --env DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \
@@ -141,21 +141,31 @@ dbus:
141
141
 
142
142
  This configuration will expose 2 methods. Triggering methods can be done by publishing json messages to the `dbus2mqtt/org.mpris.MediaPlayer2/command` MQTT topic. Arguments can be passed along in `args`.
143
143
 
144
- Note that methods are called on **all** bus_names matching the configured pattern
144
+ Some examples that call methods on **all** bus_names matching the configured pattern
145
145
 
146
146
  ```json
147
147
  {
148
- "method" : "Play",
148
+ "method": "Play",
149
149
  }
150
150
  ```
151
151
 
152
152
  ```json
153
153
  {
154
- "method" : "OpenUri",
154
+ "method": "OpenUri",
155
155
  "args": []
156
156
  }
157
157
  ```
158
158
 
159
+ To specifically target objects the properties `bus_name` and/or `path` can be used. Both properties support wildcards
160
+
161
+ ```json
162
+ {
163
+ "method": "Play",
164
+ "bus_name": "*.firefox",
165
+ "path": "/org/mpris/MediaPlayer2"
166
+ }
167
+ ```
168
+
159
169
  ### Exposing dbus signals
160
170
 
161
171
  Publishing signals to MQTT topics works by subscribing to the relevant signal and using flows for publishing
@@ -0,0 +1,37 @@
1
+ # Bluez
2
+
3
+ This configuration file demonstrates how to use dbus2mqtt to bridge D-Bus events from BlueZ (the official Linux Bluetooth protocol stack) to MQTT topics. It subscribes to relevant D-Bus signals and properties for both the Bluetooth adapter (`hci0`) and all Bluetooth devices managed by BlueZ. The configuration defines flows that:
4
+
5
+ * Monitor property changes and object lifecycle events (added/removed) for the Bluetooth adapter and devices.
6
+ * Retrieve the current state of the adapter or device using the `GetAll` method from the `org.freedesktop.DBus.Properties` interface.
7
+ * Publish the retrieved state as JSON payloads to structured MQTT topics, enabling real-time monitoring and integration with home automation or IoT systems.
8
+
9
+ This setup allows MQTT clients to receive updates about Bluetooth adapter and device states, as well as notifications when devices are removed, making it easier to integrate Bluetooth events into broader automation workflows.
10
+
11
+ Configuration activities
12
+
13
+ * dbus2mqtt setup using the supplied [bluez.yaml](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/bluez.yaml)
14
+
15
+ Execute the following command to run dbus2mqtt with the example configuration in this repository.
16
+
17
+ ```bash
18
+ uv run dbus2mqtt --config docs/examples/bluez.yaml
19
+ ```
20
+
21
+ ## Commands
22
+
23
+ The following table lists commands, their descriptions, and an example JSON payload for invoking them via MQTT.
24
+
25
+ Dbus methods can be invoked by sendig the JSON payload to MQTT topic `dbus2mqtt/bluez/hci0/command`. Method calls will be done for all matching dbus objects.
26
+
27
+ | Interface | Method<br />Property | Description | Example MQTT JSON Payload |
28
+ |----------------------|-----------------------|--------------------------------------|-------------------------------------------------|
29
+ | `org.bluez.Adapter1` | `StartDiscovery` | Starts bluetooth discovery | `{ "method": "StartDiscovery" }` |
30
+ | `org.bluez.Adapter1` | `StopDiscovery` | Stops bluetooth discovery | `{ "method": "StopDiscovery" }` |
31
+ | `org.bluez.Device1` | `Connect` | | `{ "method": "Connect", "path": "/org/bluez/hci0/dev_A1_A2_A3_A4_A5_A6" }` |
32
+ | `org.bluez.Device1` | `Disconnect` | | `{ "method": "Disconnect", "path": "/org/bluez/hci0/dev_A1_A2_A3_A4_A5_A6" }` |
33
+ | `org.bluez.Device1` | `Pair` | | `{ "method": "Pair", "path": "/org/bluez/hci0/dev_A1_A2_A3_A4_A5_A6" }` |
34
+ | `org.bluez.Device1` | `CancelPairing` | | `{ "method": "CancelPairing", "path": "/org/bluez/hci0/dev_A1_A2_A3_A4_A5_A6" }` |
35
+
36
+
37
+ https://manpages.ubuntu.com/manpages/noble/man5/org.bluez.
@@ -0,0 +1,86 @@
1
+ dbus:
2
+ bus_type: SYSTEM
3
+ subscriptions:
4
+
5
+ - bus_name: org.bluez
6
+ path: /org/bluez/hci0
7
+
8
+ # https://manpages.ubuntu.com/manpages/oracular/man5/org.bluez.Adapter.5.html
9
+
10
+ interfaces:
11
+ - interface: org.freedesktop.DBus.Properties
12
+ signals:
13
+ - signal: PropertiesChanged
14
+
15
+ - interface: org.bluez.Adapter1
16
+ mqtt_command_topic: dbus2mqtt/bluez/hci0/command
17
+ methods:
18
+ - method: StartDiscovery
19
+ - method: StopDiscovery
20
+
21
+ flows:
22
+ - name: "publish adapter state"
23
+ triggers:
24
+ - type: object_added
25
+ - type: dbus_signal
26
+ interface: org.freedesktop.DBus.Properties
27
+ signal: PropertiesChanged
28
+ actions:
29
+ - type: context_set
30
+ context:
31
+ adapter_properties: |
32
+ {{ dbus_call('org.bluez', '/org/bluez/hci0', 'org.freedesktop.DBus.Properties', 'GetAll', ['org.bluez.Adapter1']) }}
33
+ - type: mqtt_publish
34
+ topic: dbus2mqtt/bluez/hci0
35
+ payload_type: json
36
+ payload_template: |
37
+ {{
38
+ { 'dbus_object_path': path }
39
+ | combine(adapter_properties)
40
+ }}
41
+
42
+ - bus_name: org.bluez
43
+ path: /org/bluez/hci0/dev_*
44
+
45
+ # https://manpages.ubuntu.com/manpages/noble/man5/org.bluez.Device.5.html
46
+ interfaces:
47
+ - interface: org.freedesktop.DBus.Properties
48
+ signals:
49
+ - signal: PropertiesChanged
50
+
51
+ - interface: org.bluez.Device1
52
+ mqtt_command_topic: dbus2mqtt/bluez/hci0/command
53
+ methods:
54
+ - method: Connect
55
+ - method: Disconnect
56
+ - method: Pair
57
+ - method: CancelPairing
58
+
59
+ flows:
60
+ - name: "publish device state"
61
+ triggers:
62
+ - type: object_added
63
+ - type: dbus_signal
64
+ interface: org.freedesktop.DBus.Properties
65
+ signal: PropertiesChanged
66
+ actions:
67
+ - type: context_set
68
+ context:
69
+ device_properties: |
70
+ {{ dbus_call('org.bluez', path, 'org.freedesktop.DBus.Properties', 'GetAll', ['org.bluez.Device1']) }}
71
+ - type: mqtt_publish
72
+ topic: dbus2mqtt/bluez/{{ path | replace('/org/bluez/', '') }}
73
+ payload_type: json
74
+ payload_template: |
75
+ {{
76
+ { 'dbus_object_path': path }
77
+ | combine(device_properties)
78
+ }}
79
+ - name: "device removed"
80
+ triggers:
81
+ - type: object_removed
82
+ actions:
83
+ - type: mqtt_publish
84
+ topic: dbus2mqtt/bluez/{{ path | replace('/org/bluez/', '') }}
85
+ payload_type: json
86
+ payload_template: "{{ None }}"
@@ -28,7 +28,7 @@ dbus:
28
28
  flows:
29
29
  - name: "publish internal state"
30
30
  triggers:
31
- - type: bus_name_added
31
+ - type: object_added
32
32
  - type: schedule
33
33
  interval: {seconds: 5}
34
34
  actions:
@@ -19,7 +19,7 @@ Features:
19
19
  Configuration activities
20
20
 
21
21
  * MQTT Sensor and player configuration in Home Assistant (see below)
22
- * dbus2mqtt setup using the supplied `home_assistant_media_player.yaml`
22
+ * dbus2mqtt setup using the supplied [home_assistant_media_player.yaml](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.yaml)
23
23
 
24
24
  Execute the following command to run dbus2mqtt with the example configuration in this repository.
25
25
 
@@ -36,6 +36,8 @@ The following setup is known to work with Home Assistant.
36
36
  | `Firefox` | ✅ | ✅ | ✅ | ✅ | | ❌ | ✅ | ✅ | |
37
37
  | `VLC` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | |
38
38
  | `Chromium` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✔️ | Images not working when Chromium is running as snap |
39
+ | `Kodi` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | Requires Kodi plugin [MediaPlayerRemoteInterface](https://github.com/wastis/MediaPlayerRemoteInterface)<br /> Requires path to be set to '/'<br />Signals not working due to non-defaut path |
40
+
39
41
 
40
42
  More players that support MPRIS can be found here: <https://wiki.archlinux.org/title/MPRIS>
41
43
 
@@ -97,7 +99,12 @@ media_player:
97
99
  media_content_type_template: music # needed to show 'artist'
98
100
  media_duration_template: "{{ (state_attr('sensor.mpris_media_player', 'Metadata') or {}).get('mpris:length', 0) }}"
99
101
  album_template: "{{ (state_attr('sensor.mpris_media_player', 'Metadata') or {}).get('xesam:album', '') }}"
100
- artist_template: "{{ (state_attr('sensor.mpris_media_player', 'Metadata') or {}).get('xesam:artist', ['']) | first }}"
102
+ artist_template: >-
103
+ {% set artist = (state_attr('sensor.mpris_media_player', 'Metadata') or {}).get('xesam:artist', '') %}
104
+ {% if artist is string %}
105
+ {% set artist = [artist] %}
106
+ {% endif %}
107
+ {{ artist | first }}
101
108
 
102
109
  # mpris:artUrl might contain a file:// schema. In these cases we rely on images published via MQTT
103
110
  media_image_url_template: >-
@@ -42,7 +42,7 @@ dbus:
42
42
  flows:
43
43
  - name: "publish player state"
44
44
  triggers:
45
- - type: bus_name_added
45
+ - type: object_added
46
46
  - type: schedule
47
47
  interval: {seconds: 5}
48
48
  - type: dbus_signal
@@ -57,6 +57,9 @@ dbus:
57
57
  context:
58
58
  mpris_bus_name: '{{ dbus_list("org.mpris.MediaPlayer2.*") | first }}'
59
59
  mpris_path: /org/mpris/MediaPlayer2
60
+ # Some players return stale position if GetAll is executed immediately after a seeked signal.
61
+ # By storing the seeked position it can be used to override Position below
62
+ seeked_position: '{{ args[0] if trigger_type == "dbus_signal" and signal == "Seeked" else None }}'
60
63
  - type: context_set
61
64
  context:
62
65
  player_properties: |
@@ -68,13 +71,14 @@ dbus:
68
71
  {{
69
72
  { 'bus_name': mpris_bus_name }
70
73
  | combine(player_properties)
74
+ | combine ({ 'Position': seeked_position } if seeked_position else {})
71
75
  }}
72
76
 
73
77
  - name: "publish local art image"
74
78
  # mpris:artUrl can have a file:// or http:// schema
75
79
  # Home Assistant is unable to access file:// so we use MQTT for that
76
80
  triggers:
77
- - type: bus_name_added
81
+ - type: object_added
78
82
  - type: dbus_signal
79
83
  interface: org.freedesktop.DBus.Properties
80
84
  signal: PropertiesChanged
@@ -90,7 +94,7 @@ dbus:
90
94
 
91
95
  - name: "player removed"
92
96
  triggers:
93
- - type: bus_name_removed
97
+ - type: object_removed
94
98
  # filter: # TODO: Check if this is the last or inactive one #
95
99
  actions:
96
100
  - type: mqtt_publish
@@ -5,7 +5,7 @@
5
5
  Trying out this example
6
6
 
7
7
  ```bash
8
- uv run dbus2mqtt --config docs/linux_desktop.yaml
8
+ uv run dbus2mqtt --config docs/examples/linux_desktop.yaml
9
9
  ```
10
10
 
11
11
  This example shows how this dbus2mqtt can act as a bridge between the MPRIS player and Home Assistant.
@@ -25,7 +25,7 @@ dbus:
25
25
  flows:
26
26
  - name: "publish state at startup and schedule"
27
27
  triggers:
28
- - type: bus_name_added
28
+ - type: object_added
29
29
  - type: schedule
30
30
  interval: {seconds: 5}
31
31
  actions:
@@ -1,4 +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
5
  * [Publish dbus2mqtt internal state](examples/dbus2mqtt_internal_state.md)
@@ -6,8 +6,8 @@ Flows can be defined on a global or dbus subscription level and can be triggered
6
6
 
7
7
  * `schedule` for cron based schedules
8
8
  * `dbus_signal` for when dbus signal occur
9
- * `bus_name_added` when a new bus_name is registered on dbus
10
- * `bus_name_removed` when a bus_name is removed from dbus
9
+ * `object_added` when a new bus_name is registered on dbus
10
+ * `object_removed` when a bus_name is removed from dbus
11
11
 
12
12
  Within each flow a set of actions can be configured. These are executed in order
13
13
 
@@ -53,14 +53,15 @@ When triggered, the following context parameters are available
53
53
  | name | type | description |
54
54
  |------|------|-------------|
55
55
  | bus_name | string | bus_name of the object that was registered on dbus |
56
- | path | string | bus_name path of the object that was registered on dbus |
56
+ | path | string | path of the object that was registered on dbus |
57
57
  | interface | string | name of interface for which the signal was triggered |
58
+ | signal | string | name of the signal, e.g. 'Seeked'
58
59
  | args | list | signal arguments, list of objects |
59
60
 
60
- ### bus_name_added
61
+ ### object_added
61
62
 
62
63
  ```yaml
63
- type: bus_name_added
64
+ type: object_added
64
65
  ```
65
66
 
66
67
  When triggered, the following context parameters are available
@@ -68,12 +69,12 @@ When triggered, the following context parameters are available
68
69
  | name | description |
69
70
  |------|-------------|
70
71
  | bus_name | bus_name of the object that was registered on dbus |
71
- | path | bus_name path of the object that was registered on dbus |
72
+ | path | path of the object that was registered on dbus |
72
73
 
73
- ### bus_name_removed
74
+ ### object_removed
74
75
 
75
76
  ```yaml
76
- type: bus_name_removed
77
+ type: object_removed
77
78
  ```
78
79
 
79
80
  When triggered, the following context parameters are available
@@ -81,7 +82,7 @@ When triggered, the following context parameters are available
81
82
  | name | description |
82
83
  |------|-------------|
83
84
  | bus_name | bus_name of the object that was registered on dbus |
84
- | path | bus_name path of the object that was registered on dbus |
85
+ | path | path of the object that was registered on dbus |
85
86
 
86
87
  ## Flow actions
87
88
 
@@ -60,7 +60,7 @@ dev = [
60
60
  "pyright>=1.1.396",
61
61
  "pre-commit>=4.2.0",
62
62
  "pip>=25.0.1",
63
- "pytest-asyncio>=0.26.0",
63
+ "pytest-asyncio>=1.0.0"
64
64
  ]
65
65
 
66
66
  [project.urls]
@@ -81,7 +81,9 @@ lint-pyright = "pyright"
81
81
  addopts = "-s"
82
82
  testpaths = ["tests"]
83
83
  pythonpath = "."
84
- asyncio_default_fixture_loop_scope = "session"
84
+ asyncio_default_fixture_loop_scope = "function"
85
+ asyncio_default_test_loop_scope = "function"
86
+ log_cli = true
85
87
 
86
88
  [tool.pyright]
87
89
  venvPath = "."
@@ -1,5 +1,6 @@
1
1
  import fnmatch
2
2
  import uuid
3
+ import warnings
3
4
 
4
5
  from dataclasses import dataclass, field
5
6
  from typing import Annotated, Any, Literal
@@ -65,15 +66,29 @@ class FlowTriggerDbusSignalConfig:
65
66
  @dataclass
66
67
  class FlowTriggerBusNameAddedConfig:
67
68
  type: Literal["bus_name_added"] = "bus_name_added"
68
- # filter: str | None = None
69
+
70
+ def __post_init__(self):
71
+ warnings.warn(f"{self.type} flow trigger may be removed in a future version.", DeprecationWarning, stacklevel=2)
69
72
 
70
73
  @dataclass
71
74
  class FlowTriggerBusNameRemovedConfig:
72
75
  type: Literal["bus_name_removed"] = "bus_name_removed"
76
+
77
+ def __post_init__(self):
78
+ warnings.warn(f"{self.type} flow trigger may be removed in a future version.", DeprecationWarning, stacklevel=2)
79
+
80
+ @dataclass
81
+ class FlowTriggerObjectAddedConfig:
82
+ type: Literal["object_added"] = "object_added"
83
+ # filter: str | None = None
84
+
85
+ @dataclass
86
+ class FlowTriggerObjectRemovedConfig:
87
+ type: Literal["object_removed"] = "object_removed"
73
88
  # filter: str | None = None
74
89
 
75
90
  FlowTriggerConfig = Annotated[
76
- FlowTriggerMqttConfig | FlowTriggerScheduleConfig | FlowTriggerDbusSignalConfig | FlowTriggerBusNameAddedConfig | FlowTriggerBusNameRemovedConfig,
91
+ FlowTriggerMqttConfig | FlowTriggerScheduleConfig | FlowTriggerDbusSignalConfig | FlowTriggerBusNameAddedConfig | FlowTriggerBusNameRemovedConfig | FlowTriggerObjectAddedConfig | FlowTriggerObjectRemovedConfig,
77
92
  Field(discriminator="type")
78
93
  ]
79
94
 
@@ -117,6 +132,7 @@ class SubscriptionConfig:
117
132
  @dataclass
118
133
  class DbusConfig:
119
134
  subscriptions: list[SubscriptionConfig]
135
+ bus_type: Literal["SESSION", "SYSTEM"] = "SESSION"
120
136
 
121
137
  def is_bus_name_configured(self, bus_name: str) -> bool:
122
138
 
@@ -125,11 +141,14 @@ class DbusConfig:
125
141
  return True
126
142
  return False
127
143
 
128
- def get_subscription_configs(self, bus_name: str, path: str) -> list[SubscriptionConfig]:
144
+ def get_subscription_configs(self, bus_name: str, path: str|None = None) -> list[SubscriptionConfig]:
129
145
  res: list[SubscriptionConfig] = []
130
146
  for subscription in self.subscriptions:
131
- if fnmatch.fnmatchcase(bus_name, subscription.bus_name) and path == subscription.path:
132
- res.append(subscription)
147
+ if fnmatch.fnmatchcase(bus_name, subscription.bus_name):
148
+ if not path or path == subscription.path:
149
+ res.append(subscription)
150
+ elif fnmatch.fnmatchcase(path, subscription.path):
151
+ res.append(subscription)
133
152
  return res
134
153
 
135
154
  @dataclass