dbus2mqtt 0.1.1__tar.gz → 0.1.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.
- dbus2mqtt-0.1.2/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- dbus2mqtt-0.1.2/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.github/release-drafter.yml +1 -2
- dbus2mqtt-0.1.1/.github/workflows/main.yml → dbus2mqtt-0.1.2/.github/workflows/ci.yml +1 -1
- dbus2mqtt-0.1.2/.github/workflows/docker-latest.yml +74 -0
- dbus2mqtt-0.1.2/.github/workflows/docker-publish-pypi-release.yml +69 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.github/workflows/publish.yml +0 -8
- dbus2mqtt-0.1.2/.gitignore +25 -0
- dbus2mqtt-0.1.2/.python-version +1 -0
- dbus2mqtt-0.1.2/.vscode/launch.json +13 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/PKG-INFO +17 -16
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/README.md +15 -15
- dbus2mqtt-0.1.1/Dockerfile → dbus2mqtt-0.1.2/docker/Dockerfile.latest +5 -3
- dbus2mqtt-0.1.2/docker/Dockerfile.pypi +10 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/docs/examples/home_assistant_media_player.md +51 -25
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/docs/examples/home_assistant_media_player.yaml +10 -7
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/pyproject.toml +1 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/config.py +1 -1
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/dbus/dbus_client.py +11 -9
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/dbus/dbus_util.py +1 -1
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/event_broker.py +1 -2
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/tests/__init__.py +18 -1
- dbus2mqtt-0.1.2/tests/dbus/test_dbus_client.py +49 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/tests/flow/triggers/test_dbus_client_triggers.py +6 -25
- dbus2mqtt-0.1.1/.gitignore +0 -6
- dbus2mqtt-0.1.1/.python-version +0 -1
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.dockerignore +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.env.example +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.github/workflows/pre-commit.yml +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.github/workflows/release-drafter.yml +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.pre-commit-config.yaml +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.vscode/settings.json +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/.yamllint.yml +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/LICENSE +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/config-test.yaml +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/docs/debugging.md +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/docs/examples/linux_desktop.md +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/docs/examples/linux_desktop.yaml +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/docs/examples.md +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/renovate.json +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/__init__.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/__main__.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/dbus/dbus_types.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/flow/__init__.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/flow/actions/context_set.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/flow/actions/mqtt_publish.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/flow/flow_processor.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/main.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/mqtt/mqtt_client.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/template/dbus_template_functions.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/src/dbus2mqtt/template/templating.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/tests/config/test_examples.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/tests/flow/actions/test_context_set.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/tests/flow/actions/test_mqtt_publish.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/tests/flow/test_flow_processor.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/tests/template/test_templating.py +0 -0
- {dbus2mqtt-0.1.1 → dbus2mqtt-0.1.2}/uv.lock +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Describe the bug**
|
|
11
|
+
A clear and concise description of what the bug is.
|
|
12
|
+
|
|
13
|
+
**To Reproduce**
|
|
14
|
+
Steps to reproduce the behavior:
|
|
15
|
+
1. Go to '...'
|
|
16
|
+
2. Click on '....'
|
|
17
|
+
3. Scroll down to '....'
|
|
18
|
+
4. See error
|
|
19
|
+
|
|
20
|
+
**Expected behavior**
|
|
21
|
+
A clear and concise description of what you expected to happen.
|
|
22
|
+
|
|
23
|
+
**Screenshots**
|
|
24
|
+
If applicable, add screenshots to help explain your problem.
|
|
25
|
+
|
|
26
|
+
**Desktop (please complete the following information):**
|
|
27
|
+
- OS: [e.g. iOS]
|
|
28
|
+
- Browser [e.g. chrome, safari]
|
|
29
|
+
- Version [e.g. 22]
|
|
30
|
+
|
|
31
|
+
**Smartphone (please complete the following information):**
|
|
32
|
+
- Device: [e.g. iPhone6]
|
|
33
|
+
- OS: [e.g. iOS8.1]
|
|
34
|
+
- Browser [e.g. stock browser, safari]
|
|
35
|
+
- Version [e.g. 22]
|
|
36
|
+
|
|
37
|
+
**Additional context**
|
|
38
|
+
Add any other context about the problem here.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea for this project
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Is your feature request related to a problem? Please describe.**
|
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
12
|
+
|
|
13
|
+
**Describe the solution you'd like**
|
|
14
|
+
A clear and concise description of what you want to happen.
|
|
15
|
+
|
|
16
|
+
**Describe alternatives you've considered**
|
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
18
|
+
|
|
19
|
+
**Additional context**
|
|
20
|
+
Add any other context or screenshots about the feature request here.
|
|
@@ -14,8 +14,6 @@ categories:
|
|
|
14
14
|
- 'bug'
|
|
15
15
|
- title: '🧰 Maintenance'
|
|
16
16
|
label: 'chore'
|
|
17
|
-
# change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
|
18
|
-
# change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
|
|
19
17
|
exclude-contributors:
|
|
20
18
|
- jwnmulder
|
|
21
19
|
version-resolver:
|
|
@@ -29,6 +27,7 @@ version-resolver:
|
|
|
29
27
|
labels:
|
|
30
28
|
- 'patch'
|
|
31
29
|
default: patch
|
|
30
|
+
change-template: '* $TITLE (#$NUMBER)'
|
|
32
31
|
template: |
|
|
33
32
|
## Changes
|
|
34
33
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: Build and Push Docker latest
|
|
2
|
+
|
|
3
|
+
"on":
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
docker:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
packages: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
|
|
23
|
+
- name: "Set up Python"
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version-file: ".python-version"
|
|
27
|
+
|
|
28
|
+
- name: Determine version from hatch
|
|
29
|
+
id: version
|
|
30
|
+
run: echo "PRETEND_VERSION=$(uvx hatch version)" >> "$GITHUB_OUTPUT"
|
|
31
|
+
|
|
32
|
+
- name: Docker meta
|
|
33
|
+
id: meta
|
|
34
|
+
uses: docker/metadata-action@v5
|
|
35
|
+
with:
|
|
36
|
+
images: |
|
|
37
|
+
jwnmulder/dbus2mqtt
|
|
38
|
+
ghcr.io/jwnmulder/dbus2mqtt
|
|
39
|
+
tags: |
|
|
40
|
+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
|
41
|
+
labels: |
|
|
42
|
+
org.opencontainers.image.source=https://github.com/jwnmulder/dbus2mqtt
|
|
43
|
+
|
|
44
|
+
- name: Set up QEMU
|
|
45
|
+
uses: docker/setup-qemu-action@v3
|
|
46
|
+
|
|
47
|
+
- name: Set up Docker Buildx
|
|
48
|
+
uses: docker/setup-buildx-action@v3
|
|
49
|
+
|
|
50
|
+
- name: Login to DockerHub
|
|
51
|
+
if: github.event_name != 'pull_request'
|
|
52
|
+
uses: docker/login-action@v3
|
|
53
|
+
with:
|
|
54
|
+
username: jwnmulder
|
|
55
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
56
|
+
|
|
57
|
+
- name: Login to GHCR
|
|
58
|
+
if: github.event_name != 'pull_request'
|
|
59
|
+
uses: docker/login-action@v3
|
|
60
|
+
with:
|
|
61
|
+
registry: ghcr.io
|
|
62
|
+
username: jwnmulder
|
|
63
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
64
|
+
|
|
65
|
+
- name: Build and push
|
|
66
|
+
uses: docker/build-push-action@v6
|
|
67
|
+
with:
|
|
68
|
+
context: .
|
|
69
|
+
file: ./docker/Dockerfile.latest
|
|
70
|
+
push: ${{ github.event_name != 'pull_request' }}
|
|
71
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
72
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
73
|
+
build-args: |
|
|
74
|
+
PRETEND_VERSION=${{ steps.version.outputs.PRETEND_VERSION }}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Build and Push Docker Images for Released Versions
|
|
2
|
+
|
|
3
|
+
"on":
|
|
4
|
+
# push:
|
|
5
|
+
# branches:
|
|
6
|
+
# - main
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
docker:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
packages: write
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
version:
|
|
17
|
+
- 0.1
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Docker meta
|
|
24
|
+
id: meta
|
|
25
|
+
uses: docker/metadata-action@v5
|
|
26
|
+
with:
|
|
27
|
+
images: |
|
|
28
|
+
jwnmulder/dbus2mqtt
|
|
29
|
+
ghcr.io/jwnmulder/dbus2mqtt
|
|
30
|
+
tags: |
|
|
31
|
+
type=pep440,pattern={{major}},value=${{ matrix.version }},enable=${{ !startsWith(matrix.version, '0.') }}
|
|
32
|
+
type=pep440,pattern={{major}}.{{minor}},value=${{ matrix.version }}
|
|
33
|
+
# type=ref,event=branch
|
|
34
|
+
# type=ref,event=pr
|
|
35
|
+
# type=semver,pattern={{version}},value=${{ matrix.version }}
|
|
36
|
+
labels: |
|
|
37
|
+
org.opencontainers.image.source=https://github.com/jwnmulder/dbus2mqtt
|
|
38
|
+
|
|
39
|
+
- name: Set up QEMU
|
|
40
|
+
uses: docker/setup-qemu-action@v3
|
|
41
|
+
|
|
42
|
+
- name: Set up Docker Buildx
|
|
43
|
+
uses: docker/setup-buildx-action@v3
|
|
44
|
+
|
|
45
|
+
- name: Login to DockerHub
|
|
46
|
+
if: github.event_name != 'pull_request'
|
|
47
|
+
uses: docker/login-action@v3
|
|
48
|
+
with:
|
|
49
|
+
username: jwnmulder
|
|
50
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
51
|
+
|
|
52
|
+
- name: Login to GHCR
|
|
53
|
+
if: github.event_name != 'pull_request'
|
|
54
|
+
uses: docker/login-action@v3
|
|
55
|
+
with:
|
|
56
|
+
registry: ghcr.io
|
|
57
|
+
username: jwnmulder
|
|
58
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
59
|
+
|
|
60
|
+
- name: Build and push
|
|
61
|
+
uses: docker/build-push-action@v6
|
|
62
|
+
with:
|
|
63
|
+
context: .
|
|
64
|
+
file: ./docker/Dockerfile.pypi
|
|
65
|
+
push: ${{ github.event_name != 'pull_request' }}
|
|
66
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
67
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
68
|
+
build-args: |
|
|
69
|
+
DBUS2MQTT_VERSION=${{ matrix.version }}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
# This workflow will upload a Python Package to PyPI when a release is created
|
|
2
|
-
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
-
|
|
4
|
-
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
-
# They are provided by a third-party and are governed by
|
|
6
|
-
# separate terms of service, privacy policy, and support
|
|
7
|
-
# documentation.
|
|
8
|
-
|
|
9
1
|
name: Upload Python Package
|
|
10
2
|
|
|
11
3
|
"on":
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# https://github.com/github/gitignore/blob/main/Python.gitignore
|
|
2
|
+
# https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
|
3
|
+
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
dist/
|
|
11
|
+
|
|
12
|
+
# Environments
|
|
13
|
+
.env
|
|
14
|
+
.venv
|
|
15
|
+
|
|
16
|
+
# Ruff stuff:
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
|
|
19
|
+
### VisualStudioCode ###
|
|
20
|
+
.vscode/*
|
|
21
|
+
!.vscode/settings.json
|
|
22
|
+
!.vscode/tasks.json
|
|
23
|
+
!.vscode/launch.json
|
|
24
|
+
!.vscode/extensions.json
|
|
25
|
+
*.code-workspace
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.2.0",
|
|
3
|
+
"configurations": [
|
|
4
|
+
{
|
|
5
|
+
"name": "dbus2mqtt - home_assistant_media_player",
|
|
6
|
+
"type": "debugpy",
|
|
7
|
+
"request": "launch",
|
|
8
|
+
"module": "dbus2mqtt",
|
|
9
|
+
"console": "integratedTerminal",
|
|
10
|
+
"args": "--config docs/examples/home_assistant_media_player.yaml"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dbus2mqtt
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.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
|
|
@@ -9,6 +9,7 @@ License-File: LICENSE
|
|
|
9
9
|
Keywords: dbus,home-assistant,mpris,mqtt,python
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
13
|
Classifier: Operating System :: POSIX :: Linux
|
|
13
14
|
Classifier: Programming Language :: Python :: 3
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -31,8 +32,6 @@ Description-Content-Type: text/markdown
|
|
|
31
32
|
|
|
32
33
|
# dbus2mqtt
|
|
33
34
|
|
|
34
|
-
> **⚠️ Warning:** This project has no releases yet. Running from source works. Docker images and Python packages are planned but not yet available.
|
|
35
|
-
|
|
36
35
|
**dbus2mqtt** is a Python application that bridges **Linux D-Bus** with **MQTT**.
|
|
37
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**.
|
|
38
37
|
|
|
@@ -46,10 +45,15 @@ This makes it easy to integrate Linux desktop services or system signals into MQ
|
|
|
46
45
|
* 📡 Expose **D-Bus methods** for remote control via MQTT messages.
|
|
47
46
|
* 🏠 Includes example configurations for **MPRIS** and **Home Assistant Media Player** integration.
|
|
48
47
|
|
|
48
|
+
## Project status
|
|
49
|
+
|
|
50
|
+
**dbus2mqtt** is considered stable for the use-cases it has been tested against, and is actively being developed. Documentation is continuously being improved.
|
|
51
|
+
|
|
52
|
+
Initial testing has focused on MPRIS integration. A table of tested MPRIS players and their supported methods can be found here: [home_assistant_media_player.md](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md)
|
|
53
|
+
|
|
54
|
+
|
|
49
55
|
TODO list
|
|
50
56
|
|
|
51
|
-
* Create a release on PyPI
|
|
52
|
-
* Release a docker image
|
|
53
57
|
* Improve error handling when deleting message with 'retain' set. WARNING:dbus2mqtt.mqtt_client:on_message: Unexpected payload, expecting json, topic=dbus2mqtt/org.mpris.MediaPlayer2/command, payload=, error=Expecting value: line 1 column 1 (char 0)
|
|
54
58
|
* Property set only works the first time, need to restart after which the first set will work again
|
|
55
59
|
|
|
@@ -94,17 +98,17 @@ MQTT__USERNAME=
|
|
|
94
98
|
MQTT__PASSWORD=
|
|
95
99
|
```
|
|
96
100
|
|
|
97
|
-
###
|
|
98
|
-
|
|
99
|
-
To run dbus2mqtt from source (requires uv to be installed)
|
|
101
|
+
### Install and run dbus2mqtt
|
|
100
102
|
|
|
101
103
|
```bash
|
|
102
|
-
|
|
104
|
+
python -m pip install dbus2mqtt
|
|
105
|
+
dbus2mqtt --config config.yaml
|
|
103
106
|
```
|
|
104
107
|
|
|
108
|
+
|
|
105
109
|
### Run using docker with auto start behavior
|
|
106
110
|
|
|
107
|
-
To build and run dbus2mqtt using Docker with the [home_assistant_media_player.yaml](docs/examples/home_assistant_media_player.yaml) example from this repository
|
|
111
|
+
To build and run dbus2mqtt using Docker with the [home_assistant_media_player.yaml](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.yaml) example from this repository.
|
|
108
112
|
|
|
109
113
|
```bash
|
|
110
114
|
# setup configuration
|
|
@@ -112,9 +116,6 @@ mkdir -p $HOME/.config/dbus2mqtt
|
|
|
112
116
|
cp docs/examples/home_assistant_media_player.yaml $HOME/.config/dbus2mqtt/config.yaml
|
|
113
117
|
cp .env.example $HOME/.config/dbus2mqtt/.env
|
|
114
118
|
|
|
115
|
-
# build image
|
|
116
|
-
docker build -t jwnmulder/dbus2mqtt:latest .
|
|
117
|
-
|
|
118
119
|
# run image and automatically start on reboot
|
|
119
120
|
docker run --detach --name dbus2mqtt \
|
|
120
121
|
--volume "$HOME"/.config/dbus2mqtt:"$HOME"/.config/dbus2mqtt \
|
|
@@ -133,7 +134,7 @@ sudo docker logs dbus2mqtt -f
|
|
|
133
134
|
|
|
134
135
|
## Examples
|
|
135
136
|
|
|
136
|
-
This repository contains
|
|
137
|
+
This repository contains examples under [docs/examples](https://github.com/jwnmulder/dbus2mqtt/blob/main//docs/examples.md). The most complete one being [MPRIS to Home Assistant Media Player integration](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md)
|
|
137
138
|
|
|
138
139
|
## Configuration reference
|
|
139
140
|
|
|
@@ -216,8 +217,8 @@ dbus:
|
|
|
216
217
|
|
|
217
218
|
## Flows
|
|
218
219
|
|
|
219
|
-
TODO: Document flows, for now see the [MPRIS to Home Assistant Media Player integration](docs/examples/home_assistant_media_player.md) example
|
|
220
|
+
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
|
|
220
221
|
|
|
221
222
|
## Jinja templating
|
|
222
223
|
|
|
223
|
-
TODO: Document Jinja templating, for now see the [MPRIS to Home Assistant Media Player integration](docs/examples/home_assistant_media_player.md) example
|
|
224
|
+
TODO: Document Jinja templating, 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
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# dbus2mqtt
|
|
2
2
|
|
|
3
|
-
> **⚠️ Warning:** This project has no releases yet. Running from source works. Docker images and Python packages are planned but not yet available.
|
|
4
|
-
|
|
5
3
|
**dbus2mqtt** is a Python application that bridges **Linux D-Bus** with **MQTT**.
|
|
6
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**.
|
|
7
5
|
|
|
@@ -15,10 +13,15 @@ This makes it easy to integrate Linux desktop services or system signals into MQ
|
|
|
15
13
|
* 📡 Expose **D-Bus methods** for remote control via MQTT messages.
|
|
16
14
|
* 🏠 Includes example configurations for **MPRIS** and **Home Assistant Media Player** integration.
|
|
17
15
|
|
|
16
|
+
## Project status
|
|
17
|
+
|
|
18
|
+
**dbus2mqtt** is considered stable for the use-cases it has been tested against, and is actively being developed. Documentation is continuously being improved.
|
|
19
|
+
|
|
20
|
+
Initial testing has focused on MPRIS integration. A table of tested MPRIS players and their supported methods can be found here: [home_assistant_media_player.md](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md)
|
|
21
|
+
|
|
22
|
+
|
|
18
23
|
TODO list
|
|
19
24
|
|
|
20
|
-
* Create a release on PyPI
|
|
21
|
-
* Release a docker image
|
|
22
25
|
* Improve error handling when deleting message with 'retain' set. WARNING:dbus2mqtt.mqtt_client:on_message: Unexpected payload, expecting json, topic=dbus2mqtt/org.mpris.MediaPlayer2/command, payload=, error=Expecting value: line 1 column 1 (char 0)
|
|
23
26
|
* Property set only works the first time, need to restart after which the first set will work again
|
|
24
27
|
|
|
@@ -63,17 +66,17 @@ MQTT__USERNAME=
|
|
|
63
66
|
MQTT__PASSWORD=
|
|
64
67
|
```
|
|
65
68
|
|
|
66
|
-
###
|
|
67
|
-
|
|
68
|
-
To run dbus2mqtt from source (requires uv to be installed)
|
|
69
|
+
### Install and run dbus2mqtt
|
|
69
70
|
|
|
70
71
|
```bash
|
|
71
|
-
|
|
72
|
+
python -m pip install dbus2mqtt
|
|
73
|
+
dbus2mqtt --config config.yaml
|
|
72
74
|
```
|
|
73
75
|
|
|
76
|
+
|
|
74
77
|
### Run using docker with auto start behavior
|
|
75
78
|
|
|
76
|
-
To build and run dbus2mqtt using Docker with the [home_assistant_media_player.yaml](docs/examples/home_assistant_media_player.yaml) example from this repository
|
|
79
|
+
To build and run dbus2mqtt using Docker with the [home_assistant_media_player.yaml](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.yaml) example from this repository.
|
|
77
80
|
|
|
78
81
|
```bash
|
|
79
82
|
# setup configuration
|
|
@@ -81,9 +84,6 @@ mkdir -p $HOME/.config/dbus2mqtt
|
|
|
81
84
|
cp docs/examples/home_assistant_media_player.yaml $HOME/.config/dbus2mqtt/config.yaml
|
|
82
85
|
cp .env.example $HOME/.config/dbus2mqtt/.env
|
|
83
86
|
|
|
84
|
-
# build image
|
|
85
|
-
docker build -t jwnmulder/dbus2mqtt:latest .
|
|
86
|
-
|
|
87
87
|
# run image and automatically start on reboot
|
|
88
88
|
docker run --detach --name dbus2mqtt \
|
|
89
89
|
--volume "$HOME"/.config/dbus2mqtt:"$HOME"/.config/dbus2mqtt \
|
|
@@ -102,7 +102,7 @@ sudo docker logs dbus2mqtt -f
|
|
|
102
102
|
|
|
103
103
|
## Examples
|
|
104
104
|
|
|
105
|
-
This repository contains
|
|
105
|
+
This repository contains examples under [docs/examples](https://github.com/jwnmulder/dbus2mqtt/blob/main//docs/examples.md). The most complete one being [MPRIS to Home Assistant Media Player integration](https://github.com/jwnmulder/dbus2mqtt/blob/main/docs/examples/home_assistant_media_player.md)
|
|
106
106
|
|
|
107
107
|
## Configuration reference
|
|
108
108
|
|
|
@@ -185,8 +185,8 @@ dbus:
|
|
|
185
185
|
|
|
186
186
|
## Flows
|
|
187
187
|
|
|
188
|
-
TODO: Document flows, for now see the [MPRIS to Home Assistant Media Player integration](docs/examples/home_assistant_media_player.md) example
|
|
188
|
+
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
|
|
189
189
|
|
|
190
190
|
## Jinja templating
|
|
191
191
|
|
|
192
|
-
TODO: Document Jinja templating, for now see the [MPRIS to Home Assistant Media Player integration](docs/examples/home_assistant_media_player.md) example
|
|
192
|
+
TODO: Document Jinja templating, 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
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# First, build the application in the `/app` directory.
|
|
4
4
|
# See `Dockerfile` for details.
|
|
5
|
-
FROM ghcr.io/astral-sh/uv:python3.
|
|
5
|
+
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS builder
|
|
6
6
|
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
|
|
7
7
|
|
|
8
|
+
ARG PRETEND_VERSION
|
|
9
|
+
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${PRETEND_VERSION}
|
|
10
|
+
|
|
8
11
|
# Disable Python downloads, because we want to use the system interpreter
|
|
9
12
|
# across both images. If using a managed Python version, it needs to be
|
|
10
13
|
# copied from the build image into the final image; see `standalone.Dockerfile`
|
|
@@ -21,9 +24,8 @@ ADD src/ pyproject.toml uv.lock .python-version README.md /app
|
|
|
21
24
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
22
25
|
uv sync --frozen --no-dev
|
|
23
26
|
|
|
24
|
-
|
|
25
27
|
# Then, use a final image without uv
|
|
26
|
-
FROM python:3.
|
|
28
|
+
FROM python:3.13-slim-bookworm
|
|
27
29
|
# It is important to use the image that matches the builder, as the path to the
|
|
28
30
|
# Python executable must be the same, e.g., using `python:3.12-slim-bookworm`
|
|
29
31
|
# will fail.
|
|
@@ -1,49 +1,74 @@
|
|
|
1
1
|
# Mediaplayer integration with Home Assistant
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
With dbus2mqtt as a bridge between MPRIS players and Home Assistant, it becomes possible to control Linux based media players via Home Assistant.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
The Media Player Remote Interfacing Specification (MPRIS) is a standard for controlling Linux media players. It provides a mechanism for compliant media players discovery, basic playback and media player state control as well as a tracklist interface which is used to add context to the current item.
|
|
6
|
+
|
|
7
|
+
Pre-requisites:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
* Home-Assistant with a working MQTT setup, the [media_player.template](https://github.com/Sennevds/media_player.template/tree/master) plugin installed and a working MQTT setup
|
|
10
|
+
plugins installed
|
|
10
11
|
|
|
11
12
|
Features:
|
|
12
13
|
|
|
13
14
|
* dbus subscription using `org.mpris.MediaPlayer2.*` wildcard to support multiple concurrent MRPIS players
|
|
14
15
|
* Every 5 seconds, the state if the `first` known MPRIS player is published to MQTT topic `dbus2mqtt/org.mpris.MediaPlayer2/state`
|
|
15
16
|
* Every MPRIS property update immediately publishes the state to MQTT topic `dbus2mqtt/org.mpris.MediaPlayer2/state`
|
|
16
|
-
* Support for player commands (see
|
|
17
|
+
* Support for player commands (see below)
|
|
18
|
+
|
|
19
|
+
Configuration activities
|
|
20
|
+
|
|
21
|
+
* MQTT Sensor and player configuration in Home Assistant (see below)
|
|
22
|
+
* dbus2mqtt setup using the supplied `home_assistant_media_player.yaml`
|
|
23
|
+
|
|
24
|
+
Execute the following command to run dbus2mqtt with the example configuration in this repository.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dbus2mqtt --config docs/examples/home_assistant_media_player.yaml
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## Tested configurations
|
|
32
|
+
|
|
33
|
+
The following setup is known to work with Home Assistant.
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
| Application | Play<br />Pause<br /> | Stop | Next<br />Previous | Seek<br />SetPosition | Volume | Quit | Media Info | Media Image |
|
|
36
|
+
|--------------|-----------------------|------|--------------------|------|--------|------|------------|-------------|
|
|
37
|
+
| `Firefox` | ✅ | ✅ | ✅ | ✅ | | ❌ | ✅ | Youtube only |
|
|
38
|
+
| `VLC` | | | | | | | | | |
|
|
19
39
|
|
|
20
|
-
|
|
40
|
+
## Player commands
|
|
21
41
|
|
|
22
|
-
|
|
42
|
+
The following table lists player commands, their descriptions, and an example JSON payload for invoking them via MQTT.
|
|
23
43
|
|
|
24
|
-
|
|
25
|
-
|---------------------------------|------------|------------------------------------------|------------------------------------------------------------------------------------|
|
|
26
|
-
| `org.mpris.MediaPlayer2.Player` | `Pause` | Pauses playback | `{ "method": "Pause" }` |
|
|
27
|
-
| `org.mpris.MediaPlayer2.Player` | `Play` | Starts playback | `{ "method": "Play" }` |
|
|
28
|
-
| `org.mpris.MediaPlayer2.Player` | `PlayPause`| Toggles between play and pause | `{ "method": "PlayPause" }` |
|
|
29
|
-
| `org.mpris.MediaPlayer2.Player` | `OpenUri` | Opens a media file or stream by URI | `{ "method": "OpenUri", "args": ["<URI>"] }` |
|
|
30
|
-
| `org.mpris.MediaPlayer2.Player` | `Stop` | Stops playback | `{ "method": "Stop" }` |
|
|
31
|
-
| `org.mpris.MediaPlayer2` | `Quit` | Quits the media player | `{ "method": "Quit" }` |
|
|
44
|
+
Dbus methods can be invoked by sendig the JSON payload to MQTT topic `dbus2mqtt/org.mpris.MediaPlayer2/command`. Method calls will be done for all matching players
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
| Interface | Method<br />Property | Description | Example MQTT JSON Payload |
|
|
47
|
+
|---------------------------------|---------------|------------------------------------------|------------------------------------------------|
|
|
48
|
+
| `org.mpris.MediaPlayer2.Player` | `Play` | Starts playback | `{ "method": "Play" }` |
|
|
49
|
+
| `org.mpris.MediaPlayer2.Player` | `Pause` | Pauses playback | `{ "method": "Pause" }` |
|
|
50
|
+
| `org.mpris.MediaPlayer2.Player` | `PlayPause` | Toggles between play and pause | `{ "method": "PlayPause" }` |
|
|
51
|
+
| `org.mpris.MediaPlayer2.Player` | `Next` | Next | `{ "method": "Next" }` |
|
|
52
|
+
| `org.mpris.MediaPlayer2.Player` | `Previous` | Previous | `{ "method": "Previous" }` |
|
|
53
|
+
| `org.mpris.MediaPlayer2.Player` | `Stop` | Stops playback | `{ "method": "Stop" }` |
|
|
54
|
+
| `org.mpris.MediaPlayer2.Player` | `Seek` | Seek forward or backward in micro seconds | `{ "method": "Seek", "args": [60000000] }` |
|
|
55
|
+
| `org.mpris.MediaPlayer2.Player` | `Volume` | Set volume | `{ "property": "Volume", "value": 50 }` |
|
|
56
|
+
| `org.mpris.MediaPlayer2.Player` | `SetPosition` | Set / seek to position in micro seconds. First arguments needs to be trackid which can be determined via Metadata.mpris:trackid | `{ "method": "SetPosition", "args": ["/org/mpris/MediaPlayer2/firefox", 170692139] }` |
|
|
57
|
+
| `org.mpris.MediaPlayer2` | `Quit` | Quits the media player | `{ "method": "Quit" }` |
|
|
34
58
|
|
|
35
|
-
For an overview of MPRIS commands have a look at <https://
|
|
59
|
+
For an overview of MPRIS commands have a look at <https://mpris2.readthedocs.io/en/latest/interfaces.html#mpris2.MediaPlayer2>
|
|
36
60
|
|
|
37
61
|
## Home Assistant configuration
|
|
38
62
|
|
|
39
|
-
|
|
63
|
+
The configuration shown below creates a few components in Home Assistant
|
|
40
64
|
|
|
41
|
-
|
|
65
|
+
* Media Player
|
|
66
|
+
* MQTT sensor listening on topic `dbus2mqtt/org.mpris.MediaPlayer2/state`
|
|
42
67
|
|
|
43
68
|
```yaml
|
|
44
69
|
mqtt:
|
|
45
70
|
sensor:
|
|
46
|
-
- name:
|
|
71
|
+
- name: MPRIS Media Player
|
|
47
72
|
state_topic: dbus2mqtt/org.mpris.MediaPlayer2/state
|
|
48
73
|
json_attributes_topic: dbus2mqtt/org.mpris.MediaPlayer2/state
|
|
49
74
|
value_template: >-
|
|
@@ -62,7 +87,7 @@ media_player:
|
|
|
62
87
|
- platform: media_player_template
|
|
63
88
|
media_players:
|
|
64
89
|
mpris_media_player:
|
|
65
|
-
|
|
90
|
+
device_class: generic
|
|
66
91
|
friendly_name: MPRIS Media Player
|
|
67
92
|
value_template: "{{ states('sensor.mpris_media_player') }}"
|
|
68
93
|
|
|
@@ -80,7 +105,7 @@ media_player:
|
|
|
80
105
|
media_image_url_template: >-
|
|
81
106
|
{{ state_attr('sensor.mpris_media_player', 'Metadata')['xesam:url']
|
|
82
107
|
| regex_replace(
|
|
83
|
-
find='https:\/\/www\\.youtube\\.com\/watch\\?v=(.*
|
|
108
|
+
find='https:\/\/www\\.youtube\\.com\/watch\\?v=([^&]+).*',
|
|
84
109
|
replace='https://img.youtube.com/vi/\\1/maxresdefault.jpg'
|
|
85
110
|
)
|
|
86
111
|
}}
|
|
@@ -119,7 +144,8 @@ media_player:
|
|
|
119
144
|
service: mqtt.publish
|
|
120
145
|
data:
|
|
121
146
|
topic: dbus2mqtt/org.mpris.MediaPlayer2/command
|
|
122
|
-
payload:
|
|
147
|
+
payload: >-
|
|
148
|
+
{ "method": "SetPosition", "args": ["{{ state_attr('sensor.mpris_media_player', 'Metadata')['mpris:trackid'] }}", {{ position | int }}] }
|
|
123
149
|
set_volume:
|
|
124
150
|
service: mqtt.publish
|
|
125
151
|
data:
|
|
@@ -7,12 +7,13 @@ dbus:
|
|
|
7
7
|
|
|
8
8
|
- bus_name: org.mpris.MediaPlayer2.*
|
|
9
9
|
path: /org/mpris/MediaPlayer2
|
|
10
|
-
# https://
|
|
10
|
+
# https://mpris2.readthedocs.io/en/latest/interfaces.html#mpris2.MediaPlayer2
|
|
11
11
|
|
|
12
12
|
interfaces:
|
|
13
13
|
- interface: org.freedesktop.DBus.Properties
|
|
14
14
|
signals:
|
|
15
15
|
- signal: PropertiesChanged
|
|
16
|
+
# TODO: Determine if we should filter here or in flows? The only consumers right now are the flows
|
|
16
17
|
filter: "{{ args[0] == 'org.mpris.MediaPlayer2.Player' }}"
|
|
17
18
|
methods:
|
|
18
19
|
- method: GetAll
|
|
@@ -31,6 +32,7 @@ dbus:
|
|
|
31
32
|
- method: Next
|
|
32
33
|
- method: Previous
|
|
33
34
|
- method: Seek
|
|
35
|
+
- method: SetPosition
|
|
34
36
|
- method: PlayPause
|
|
35
37
|
- method: OpenUri
|
|
36
38
|
|
|
@@ -40,16 +42,17 @@ dbus:
|
|
|
40
42
|
- type: bus_name_added
|
|
41
43
|
- type: schedule
|
|
42
44
|
interval: {seconds: 5}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
- type: dbus_signal
|
|
46
|
+
interface: org.freedesktop.DBus.Properties
|
|
47
|
+
signal: PropertiesChanged
|
|
46
48
|
# filter: "{{ args[0] == 'org.mpris.MediaPlayer2.Player' }}"
|
|
47
49
|
actions:
|
|
48
50
|
- type: context_set
|
|
49
51
|
context:
|
|
50
52
|
mpris_bus_name: '{{ dbus_list("org.mpris.MediaPlayer2.*") | first }}'
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
+
# TODO: This would be a nice addition to avoid repetition
|
|
54
|
+
# player_properties: |
|
|
55
|
+
# {{ dbus_call(mpris_bus_name, path, 'org.freedesktop.DBus.Properties', 'GetAll', ['org.mpris.MediaPlayer2.Player']) | to_yaml }}
|
|
53
56
|
path: /org/mpris/MediaPlayer2
|
|
54
57
|
- type: mqtt_publish
|
|
55
58
|
topic: dbus2mqtt/org.mpris.MediaPlayer2/state
|
|
@@ -61,7 +64,7 @@ dbus:
|
|
|
61
64
|
- name: "MPRIS player removed"
|
|
62
65
|
triggers:
|
|
63
66
|
- type: bus_name_removed
|
|
64
|
-
# filter: #
|
|
67
|
+
# filter: # TODO: Check if this is the last or inactive one #
|
|
65
68
|
actions:
|
|
66
69
|
- type: mqtt_publish
|
|
67
70
|
topic: dbus2mqtt/org.mpris.MediaPlayer2/state
|
|
@@ -125,7 +125,7 @@ class DbusConfig:
|
|
|
125
125
|
def get_subscription_configs(self, bus_name: str, path: str) -> list[SubscriptionConfig]:
|
|
126
126
|
res: list[SubscriptionConfig] = []
|
|
127
127
|
for subscription in self.subscriptions:
|
|
128
|
-
if fnmatch.fnmatchcase(bus_name, subscription.bus_name) and
|
|
128
|
+
if fnmatch.fnmatchcase(bus_name, subscription.bus_name) and path == subscription.path:
|
|
129
129
|
res.append(subscription)
|
|
130
130
|
return res
|
|
131
131
|
|
|
@@ -124,7 +124,7 @@ class DbusClient:
|
|
|
124
124
|
|
|
125
125
|
on_signal_method_name = "on_" + camel_to_snake(signal_config.signal)
|
|
126
126
|
dbus_signal_state = {
|
|
127
|
-
"
|
|
127
|
+
"bus_name": bus_name,
|
|
128
128
|
"path": path,
|
|
129
129
|
"interface_name": interface.name,
|
|
130
130
|
"subscription_config": subscription_config,
|
|
@@ -354,6 +354,8 @@ class DbusClient:
|
|
|
354
354
|
|
|
355
355
|
async def _handle_on_dbus_signal(self, signal: DbusSignalWithState):
|
|
356
356
|
|
|
357
|
+
logger.debug(f"dbus_signal: signal={signal.signal_config.signal}, args={signal.args}, bus_name={signal.bus_name}, path={signal.path}, interface={signal.interface_name}")
|
|
358
|
+
|
|
357
359
|
for flow in signal.subscription_config.flows:
|
|
358
360
|
for trigger in flow.triggers:
|
|
359
361
|
if trigger.type == "dbus_signal" and signal.signal_config.signal == trigger.signal:
|
|
@@ -366,7 +368,7 @@ class DbusClient:
|
|
|
366
368
|
|
|
367
369
|
if matches_filter:
|
|
368
370
|
context = {
|
|
369
|
-
"bus_name": signal.
|
|
371
|
+
"bus_name": signal.bus_name,
|
|
370
372
|
"path": signal.path,
|
|
371
373
|
"interface": signal.interface_name,
|
|
372
374
|
"args": signal.args
|
|
@@ -394,8 +396,8 @@ class DbusClient:
|
|
|
394
396
|
return
|
|
395
397
|
|
|
396
398
|
logger.debug(f"on_mqtt_msg: topic={msg.topic}, payload={json.dumps(msg.payload)}")
|
|
397
|
-
|
|
398
|
-
|
|
399
|
+
matched_method = False
|
|
400
|
+
matched_property = False
|
|
399
401
|
|
|
400
402
|
payload_method = msg.payload.get("method")
|
|
401
403
|
payload_method_args = msg.payload.get("args") or []
|
|
@@ -417,27 +419,27 @@ class DbusClient:
|
|
|
417
419
|
# filter configured method, configured topic, ...
|
|
418
420
|
if method.method == payload_method:
|
|
419
421
|
interface = proxy_object.get_interface(name=interface_config.interface)
|
|
422
|
+
matched_method = True
|
|
420
423
|
|
|
421
424
|
try:
|
|
422
425
|
logger.info(f"on_mqtt_msg: method={method.method}, args={payload_method_args}, bus_name={bus_name}, path={path}, interface={interface_config.interface}")
|
|
423
426
|
await self.call_dbus_interface_method(interface, method.method, payload_method_args)
|
|
424
|
-
calls_done.append(method.method)
|
|
425
427
|
except Exception as e:
|
|
426
|
-
logger.warning(f"on_mqtt_msg: method={method.method}, bus_name={bus_name} failed, exception={e}")
|
|
428
|
+
logger.warning(f"on_mqtt_msg: method={method.method}, args={payload_method_args}, bus_name={bus_name} failed, exception={e}")
|
|
427
429
|
|
|
428
430
|
for property in interface_config.properties:
|
|
429
431
|
# filter configured property, configured topic, ...
|
|
430
432
|
if property.property == payload_property:
|
|
431
433
|
interface = proxy_object.get_interface(name=interface_config.interface)
|
|
434
|
+
matched_property = True
|
|
432
435
|
|
|
433
436
|
try:
|
|
434
437
|
logger.info(f"on_mqtt_msg: property={property.property}, value={payload_value}, bus_name={bus_name}, path={path}, interface={interface_config.interface}")
|
|
435
438
|
await self.set_dbus_interface_property(interface, property.property, payload_value)
|
|
436
|
-
properties_set.append(property.property)
|
|
437
439
|
except Exception as e:
|
|
438
|
-
logger.warning(f"on_mqtt_msg: property={property.property}, bus_name={bus_name} failed, exception={e}")
|
|
440
|
+
logger.warning(f"on_mqtt_msg: property={property.property}, value={payload_value}, bus_name={bus_name} failed, exception={e}")
|
|
439
441
|
|
|
440
|
-
if
|
|
442
|
+
if not matched_method and not matched_property:
|
|
441
443
|
if payload_method:
|
|
442
444
|
logger.info(f"No configured or active dbus subscriptions for topic={msg.topic}, method={payload_method}, active bus_names={list(self.subscriptions.keys())}")
|
|
443
445
|
if payload_property:
|
|
@@ -13,7 +13,6 @@ from dbus2mqtt.config import (
|
|
|
13
13
|
SignalConfig,
|
|
14
14
|
SubscriptionConfig,
|
|
15
15
|
)
|
|
16
|
-
from dbus2mqtt.dbus.dbus_types import BusNameSubscriptions
|
|
17
16
|
|
|
18
17
|
logger = logging.getLogger(__name__)
|
|
19
18
|
|
|
@@ -26,7 +25,7 @@ class MqttMessage:
|
|
|
26
25
|
|
|
27
26
|
@dataclass
|
|
28
27
|
class DbusSignalWithState:
|
|
29
|
-
|
|
28
|
+
bus_name: str
|
|
30
29
|
path: str
|
|
31
30
|
interface_name: str
|
|
32
31
|
subscription_config: SubscriptionConfig
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
|
|
2
|
+
import dbus_next.aio as dbus_aio
|
|
3
|
+
|
|
2
4
|
from pydantic import SecretStr
|
|
3
5
|
|
|
4
6
|
from dbus2mqtt import AppContext, config
|
|
@@ -8,8 +10,9 @@ from dbus2mqtt.config import (
|
|
|
8
10
|
FlowTriggerConfig,
|
|
9
11
|
InterfaceConfig,
|
|
10
12
|
)
|
|
13
|
+
from dbus2mqtt.dbus.dbus_client import DbusClient
|
|
11
14
|
from dbus2mqtt.event_broker import EventBroker
|
|
12
|
-
from dbus2mqtt.flow.flow_processor import FlowProcessor
|
|
15
|
+
from dbus2mqtt.flow.flow_processor import FlowProcessor, FlowScheduler
|
|
13
16
|
from dbus2mqtt.template.templating import TemplateEngine
|
|
14
17
|
|
|
15
18
|
|
|
@@ -51,3 +54,17 @@ def mocked_flow_processor(app_context: AppContext, trigger_config: FlowTriggerCo
|
|
|
51
54
|
|
|
52
55
|
processor = FlowProcessor(app_context)
|
|
53
56
|
return processor, flow_config
|
|
57
|
+
|
|
58
|
+
class MockedMessageBus(dbus_aio.message_bus.MessageBus):
|
|
59
|
+
def _setup_socket(self):
|
|
60
|
+
self._stream = ""
|
|
61
|
+
self._sock = ""
|
|
62
|
+
self._fd = ""
|
|
63
|
+
|
|
64
|
+
def mocked_dbus_client(app_context: AppContext):
|
|
65
|
+
|
|
66
|
+
bus = MockedMessageBus(bus_address="unix:path=/run/user/1000/bus")
|
|
67
|
+
flow_scheduler = FlowScheduler(app_context)
|
|
68
|
+
|
|
69
|
+
dbus_client = DbusClient(app_context, bus, flow_scheduler)
|
|
70
|
+
return dbus_client
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
import dbus_next.introspection as dbus_intr
|
|
3
|
+
import dbus_next.signature as dbus_signature
|
|
4
|
+
|
|
5
|
+
from dbus_next.constants import ArgDirection
|
|
6
|
+
|
|
7
|
+
from tests import mocked_app_context, mocked_dbus_client
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_signal_handler_unwrap_args():
|
|
11
|
+
|
|
12
|
+
app_context = mocked_app_context()
|
|
13
|
+
dbus_client = mocked_dbus_client(app_context)
|
|
14
|
+
|
|
15
|
+
dbus_signal = dbus_intr.Signal("PropertiesChanged", [
|
|
16
|
+
dbus_intr.Arg(name="interface_name", signature="s", direction=[ArgDirection.IN]),
|
|
17
|
+
dbus_intr.Arg(name="changed_properties", signature="a{sv}", direction=[ArgDirection.IN]),
|
|
18
|
+
dbus_intr.Arg(name="invalidated_properties", signature="as", direction=[ArgDirection.IN])
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
dbus_signal_state = {}
|
|
22
|
+
dbus_signal_state = {
|
|
23
|
+
"bus_name": "org.mpris.MediaPlayer2.vlc",
|
|
24
|
+
"path": "/org/mpris/MediaPlayer2",
|
|
25
|
+
"interface_name": "org.freedesktop.DBus.Properties",
|
|
26
|
+
"subscription_config": None, # subscription_config,
|
|
27
|
+
"signal_config": None, # signal_config,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
handler = dbus_client._dbus_next_signal_handler(dbus_signal, dbus_signal_state)
|
|
31
|
+
assert handler is not None
|
|
32
|
+
|
|
33
|
+
args = [
|
|
34
|
+
"org.mpris.MediaPlayer2.Player",
|
|
35
|
+
{
|
|
36
|
+
"CanPause": dbus_signature.Variant("b", True)
|
|
37
|
+
},
|
|
38
|
+
[]
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# Invoke with wrapped arguments
|
|
42
|
+
handler(*args)
|
|
43
|
+
|
|
44
|
+
# Check if message is published on the event_broker
|
|
45
|
+
mqtt_message = app_context.event_broker.dbus_signal_queue.sync_q.get_nowait()
|
|
46
|
+
|
|
47
|
+
# message args should be unwrapped
|
|
48
|
+
assert mqtt_message is not None
|
|
49
|
+
assert mqtt_message.args == ["org.mpris.MediaPlayer2.Player", {"CanPause" : True}, []]
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import dbus_next.aio as dbus_aio
|
|
3
1
|
import pytest
|
|
4
2
|
|
|
5
|
-
from dbus2mqtt import AppContext
|
|
6
3
|
from dbus2mqtt.config import (
|
|
7
4
|
FlowActionContextSetConfig,
|
|
8
5
|
FlowTriggerBusNameAddedConfig,
|
|
@@ -10,10 +7,8 @@ from dbus2mqtt.config import (
|
|
|
10
7
|
FlowTriggerDbusSignalConfig,
|
|
11
8
|
SignalConfig,
|
|
12
9
|
)
|
|
13
|
-
from dbus2mqtt.
|
|
14
|
-
from
|
|
15
|
-
from dbus2mqtt.flow.flow_processor import FlowScheduler
|
|
16
|
-
from tests import mocked_app_context, mocked_flow_processor
|
|
10
|
+
from dbus2mqtt.event_broker import DbusSignalWithState
|
|
11
|
+
from tests import mocked_app_context, mocked_dbus_client, mocked_flow_processor
|
|
17
12
|
|
|
18
13
|
|
|
19
14
|
@pytest.mark.asyncio
|
|
@@ -32,7 +27,7 @@ async def test_bus_name_added_trigger():
|
|
|
32
27
|
)
|
|
33
28
|
])
|
|
34
29
|
|
|
35
|
-
dbus_client =
|
|
30
|
+
dbus_client = mocked_dbus_client(app_context)
|
|
36
31
|
|
|
37
32
|
subscription_config = app_context.config.dbus.subscriptions[0]
|
|
38
33
|
|
|
@@ -64,7 +59,7 @@ async def test_bus_name_removed_trigger():
|
|
|
64
59
|
)
|
|
65
60
|
])
|
|
66
61
|
|
|
67
|
-
dbus_client =
|
|
62
|
+
dbus_client = mocked_dbus_client(app_context)
|
|
68
63
|
|
|
69
64
|
subscription_config = app_context.config.dbus.subscriptions[0]
|
|
70
65
|
|
|
@@ -104,10 +99,10 @@ async def test_dbus_signal_trigger():
|
|
|
104
99
|
|
|
105
100
|
subscription_config = app_context.config.dbus.subscriptions[0]
|
|
106
101
|
|
|
107
|
-
dbus_client =
|
|
102
|
+
dbus_client = mocked_dbus_client(app_context)
|
|
108
103
|
|
|
109
104
|
signal = DbusSignalWithState(
|
|
110
|
-
|
|
105
|
+
bus_name="test-bus-name",
|
|
111
106
|
path="/",
|
|
112
107
|
interface_name=subscription_config.interfaces[0].interface,
|
|
113
108
|
subscription_config=subscription_config,
|
|
@@ -132,17 +127,3 @@ async def test_dbus_signal_trigger():
|
|
|
132
127
|
"interface": "test-interface-name",
|
|
133
128
|
"args": ["first-arg", "second-arg"]
|
|
134
129
|
}
|
|
135
|
-
|
|
136
|
-
class MockedMessageBus(dbus_aio.message_bus.MessageBus):
|
|
137
|
-
def _setup_socket(self):
|
|
138
|
-
self._stream = ""
|
|
139
|
-
self._sock = ""
|
|
140
|
-
self._fd = ""
|
|
141
|
-
|
|
142
|
-
def _mocked_dbus_client(app_context: AppContext):
|
|
143
|
-
|
|
144
|
-
bus = MockedMessageBus(bus_address="unix:path=/run/user/1000/bus")
|
|
145
|
-
flow_scheduler = FlowScheduler(app_context)
|
|
146
|
-
|
|
147
|
-
dbus_client = DbusClient(app_context, bus, flow_scheduler)
|
|
148
|
-
return dbus_client
|
dbus2mqtt-0.1.1/.gitignore
DELETED
dbus2mqtt-0.1.1/.python-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.12
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|