updates2mqtt 1.3.7__tar.gz → 1.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.
Files changed (62) hide show
  1. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.github/dependabot.yml +6 -0
  2. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.github/workflows/docker-publish.yml +2 -2
  3. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.github/workflows/pypi-publish.yml +7 -4
  4. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.github/workflows/python-package.yml +26 -3
  5. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.gitignore +1 -0
  6. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.pre-commit-config.yaml +5 -5
  7. updates2mqtt-1.4.1/CHANGELOG.md +22 -0
  8. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/Dockerfile +1 -1
  9. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/PKG-INFO +69 -23
  10. updates2mqtt-1.4.1/README.md +128 -0
  11. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/conftest.py +68 -5
  12. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/configuration.md +50 -19
  13. updates2mqtt-1.4.1/docs/home_assistant.md +34 -0
  14. updates2mqtt-1.4.1/docs/images/ha_entities.png +0 -0
  15. updates2mqtt-1.4.1/docs/images/ha_update_dialog.png +0 -0
  16. updates2mqtt-1.4.1/docs/images/logo-blank-256x256.png +0 -0
  17. updates2mqtt-1.4.1/docs/installation.md +57 -0
  18. updates2mqtt-1.4.1/docs/troubleshooting.md +163 -0
  19. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/examples/config.yaml.maximal +1 -0
  20. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/mkdocs.yml +24 -1
  21. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/pyproject.toml +10 -2
  22. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/app.py +15 -10
  23. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/config.py +1 -0
  24. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/integrations/docker.py +11 -5
  25. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/integrations/git_utils.py +3 -1
  26. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/model.py +4 -0
  27. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/mqtt.py +54 -9
  28. updates2mqtt-1.4.1/tests/test_app.py +130 -0
  29. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/tests/test_docker.py +8 -7
  30. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/tests/test_mqtt.py +9 -8
  31. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/uv.lock +370 -124
  32. updates2mqtt-1.3.7/CHANGELOG.md +0 -12
  33. updates2mqtt-1.3.7/README.md +0 -82
  34. updates2mqtt-1.3.7/docs/installation.md +0 -29
  35. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.dockerignore +0 -0
  36. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.hintrc +0 -0
  37. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.python-version +0 -0
  38. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/.safety-project.ini +0 -0
  39. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/LICENSE +0 -0
  40. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/SECURITY.md +0 -0
  41. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/common_packages.yaml +0 -0
  42. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/examples/config_maximal.md +0 -0
  43. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/examples/config_minimal.md +0 -0
  44. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/examples/docker_compose.md +0 -0
  45. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/images/ha_mqtt_discovery.png +0 -0
  46. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/images/ha_update_detail.png +0 -0
  47. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/images/ha_update_page.png +0 -0
  48. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/index.md +0 -0
  49. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/docs/local_builds.md +0 -0
  50. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/examples/config.yaml.minimal +0 -0
  51. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/examples/docker-compose.yaml +0 -0
  52. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/no_config.yaml +0 -0
  53. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/refresh-deps.sh +0 -0
  54. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/scripts/healthcheck.sh +0 -0
  55. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/__init__.py +0 -0
  56. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/__main__.py +0 -0
  57. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/hass_formatter.py +0 -0
  58. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/integrations/__init__.py +0 -0
  59. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/src/updates2mqtt/py.typed +0 -0
  60. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/tests/__init__.py +0 -0
  61. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/tests/test_config.py +0 -0
  62. {updates2mqtt-1.3.7 → updates2mqtt-1.4.1}/tests/test_git_utils.py +0 -0
@@ -9,16 +9,22 @@ updates:
9
9
  directory: "/" # Location of package manifests
10
10
  schedule:
11
11
  interval: "weekly"
12
+ cooldown:
13
+ default-days: 7
12
14
 
13
15
  - package-ecosystem: "github-actions"
14
16
  directory: "/"
15
17
  schedule:
16
18
  interval: "monthly"
19
+ cooldown:
20
+ default-days: 7
17
21
 
18
22
  - package-ecosystem: "docker"
19
23
  directory: "/"
20
24
  schedule:
21
25
  interval: "weekly"
26
+ cooldown:
27
+ default-days: 7
22
28
 
23
29
 
24
30
 
@@ -25,7 +25,7 @@ jobs:
25
25
  #
26
26
  steps:
27
27
  - name: Checkout repository
28
- uses: actions/checkout@v5
28
+ uses: actions/checkout@v6
29
29
  # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
30
30
  - name: Log in to the Container registry
31
31
  uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
@@ -42,7 +42,7 @@ jobs:
42
42
  uses: docker/setup-buildx-action@v3
43
43
  - name: Extract metadata (tags, labels) for Docker
44
44
  id: meta
45
- uses: docker/metadata-action@032a4b3bda1b716928481836ac5bfe36e1feaad6
45
+ uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051
46
46
  with:
47
47
  images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
48
48
  tags: |
@@ -2,13 +2,16 @@ name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
2
2
 
3
3
  on: push
4
4
 
5
+ permissions:
6
+ contents: read
7
+
5
8
  jobs:
6
9
  build:
7
10
  name: Build distribution 📦
8
11
  runs-on: ubuntu-latest
9
12
 
10
13
  steps:
11
- - uses: actions/checkout@v5
14
+ - uses: actions/checkout@v6
12
15
  with:
13
16
  persist-credentials: false
14
17
  - name: uv-setup
@@ -22,7 +25,7 @@ jobs:
22
25
  - name: Build a binary wheel and a source tarball
23
26
  run: uv build
24
27
  - name: Store the distribution packages
25
- uses: actions/upload-artifact@v4
28
+ uses: actions/upload-artifact@v5
26
29
  with:
27
30
  name: python-package-distributions
28
31
  path: dist/
@@ -42,7 +45,7 @@ jobs:
42
45
 
43
46
  steps:
44
47
  - name: Download all the dists
45
- uses: actions/download-artifact@v4
48
+ uses: actions/download-artifact@v6
46
49
  with:
47
50
  name: python-package-distributions
48
51
  path: dist/
@@ -64,7 +67,7 @@ jobs:
64
67
 
65
68
  steps:
66
69
  - name: Download all the dists
67
- uses: actions/download-artifact@v4
70
+ uses: actions/download-artifact@v6
68
71
  with:
69
72
  name: python-package-distributions
70
73
  path: dist/
@@ -15,7 +15,7 @@ jobs:
15
15
  permissions:
16
16
  contents: write
17
17
  steps:
18
- - uses: actions/checkout@v5
18
+ - uses: actions/checkout@v6
19
19
  - name: uv-setup
20
20
  # Install a specific uv version using the installer
21
21
  run: curl -LsSf https://astral.sh/uv/install.sh | sh
@@ -25,18 +25,41 @@ jobs:
25
25
  python-version-file: "pyproject.toml"
26
26
  - name: Install dependencies
27
27
  run: uv sync --all-extras --dev
28
+
28
29
  - name: Ruff
29
- uses: chartboost/ruff-action@v1
30
+ uses: astral-sh/ruff-action@v3
30
31
  - name: Test with pytest
31
32
  run: uv run pytest tests
32
33
  - name: Build a binary wheel and a source tarball
33
34
  run: uv build
35
+ - name: Generate badges
36
+ env:
37
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38
+ run: |
39
+ uv run genbadge coverage -i cov.xml -o badges/coverage.svg
40
+ uv run genbadge tests -i junit/test-results.xml -o badges/tests.svg
41
+
42
+ git config --global user.name "github-actions[bot]"
43
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
44
+ TEMP_DIR=$(mktemp -d)
45
+ cd $TEMP_DIR
46
+ git clone --single-branch --branch badges https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git . 2>/dev/null
47
+ cp -r ${{ github.workspace }}/badges .
48
+ git add badges
49
+ if git commit -m "Update badges [skip ci]"; then
50
+ git push origin badges --force
51
+ echo "Badges updated successfully"
52
+ else
53
+ echo "No changes to badges"
54
+ fi
55
+
34
56
  - name: Prep for mkdocs
35
- run: uv pip compile pyproject.toml -o requirements_docs.txt --group mkdocs
57
+ run: uv pip compile pyproject.toml -o requirements_docs.txt --group mkdocs
36
58
  - name: Deploy docs
37
59
  uses: mhausenblas/mkdocs-deploy-gh-pages@master
38
60
  env:
39
61
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62
+ EXTRA_PACKAGES: pngquant
40
63
  REQUIREMENTS: requirements_docs.txt
41
64
  CUSTOM_DOMAIN: updates2mqtt.rhizomatics.org.uk
42
65
 
@@ -11,6 +11,7 @@ htmlcov/
11
11
  lcov.info
12
12
  .vscode/
13
13
  .DS_Store
14
+ junit/
14
15
 
15
16
  # C extensions
16
17
  *.so
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/asottile/pyupgrade
3
- rev: v3.21.1
3
+ rev: v3.21.2
4
4
  hooks:
5
5
  - id: pyupgrade
6
6
  args: [--py313-plus]
@@ -17,11 +17,11 @@ repos:
17
17
  - id: check-shebang-scripts-are-executable
18
18
  name: Check shell scripts are executable
19
19
  - repo: https://github.com/astral-sh/uv-pre-commit
20
- rev: 0.9.9
20
+ rev: 0.9.13
21
21
  hooks:
22
22
  - id: uv-lock
23
23
  - repo: https://github.com/astral-sh/ruff-pre-commit
24
- rev: v0.14.4
24
+ rev: v0.14.7
25
25
  hooks:
26
26
  # Run the linter.
27
27
  - id: ruff-check
@@ -39,7 +39,7 @@ repos:
39
39
  exclude_types: [csv, json]
40
40
 
41
41
  - repo: https://github.com/pre-commit/mirrors-mypy
42
- rev: v1.18.2
42
+ rev: v1.19.0
43
43
  hooks:
44
44
  - id: mypy
45
45
  args:
@@ -64,7 +64,7 @@ repos:
64
64
  - usingversion
65
65
 
66
66
  - repo: https://github.com/PyCQA/bandit
67
- rev: '1.8.6'
67
+ rev: '1.9.2'
68
68
  hooks:
69
69
  - id: bandit
70
70
  args: ["-c", "pyproject.toml"]
@@ -0,0 +1,22 @@
1
+ # CHANGELOG
2
+
3
+ ## 1.4.1
4
+ - More logging for Docker discovery on why Home Assistant doesn't show an update button
5
+ - More test cases
6
+ - `MqttClient` is now `MqttPublisher` to avoid confusion with actual MQTT client
7
+ - Task cleanup now only interrupts explicit list of tasks - healthcheck and discovery tasks
8
+ ## 1.4.0
9
+ - MQTT protocol can now be set, to one of `3.1`,`3.11` or `5`
10
+ - Debug messages now provided for `on_subscribe` and `on_unsubscribe` callbacks
11
+ - Troubleshooting, installation, configuration docs updated, images optimized
12
+
13
+ ## 1.3.7
14
+ - Improved initial setup when run without config or env vars for MQTT
15
+ - Minor test deps update and pyproject docs
16
+
17
+ ## 1.3.6
18
+ - Changed exit code on graceful shutdown to 143
19
+ - App now exits if the MQTT username / password is not authorized
20
+ - Improved handling of env vars, default config now assumes MQTT_HOST etc unless overridden
21
+ - Will now run without a config if correct `MQTT_HOST`,`MQTT_USER`,`MQTT_PASS`,`MQTT_PORT` env vars set or match the defaults (`127.0.0.1:1883`)
22
+ - Deps update
@@ -30,7 +30,7 @@ ADD scripts/healthcheck.sh /app
30
30
  RUN chmod ug+x /app/healthcheck.sh
31
31
  ADD README.md /app/README.md
32
32
  ADD common_packages.yaml /app
33
-
33
+ RUN mkdir /app/.cache && chmod a+rw /app/.cache
34
34
 
35
35
  RUN uv sync --locked
36
36
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: updates2mqtt
3
- Version: 1.3.7
3
+ Version: 1.4.1
4
4
  Summary: System update and docker image notification and execution over MQTT
5
5
  Project-URL: Homepage, https://updates2mqtt.rhizomatics.org.uk
6
6
  Project-URL: Repository, https://github.com/rhizomatics/updates2mqtt
@@ -39,8 +39,12 @@ Description-Content-Type: text/markdown
39
39
 
40
40
  # updates2mqtt
41
41
 
42
+ [![Rhizomatics Open Source](https://img.shields.io/badge/rhizomatics%20open%20source-lightseagreen)](https://github.com/rhizomatics)
43
+
42
44
  [![PyPI - Version](https://img.shields.io/pypi/v/updates2mqtt)](https://pypi.org/project/updates2mqtt/)
43
45
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/rhizomatics/supernotify)
46
+ ![Coverage](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/ff84fbfac358820cf9c9e9e582c063cd1a137116/badges/coverage.svg)
47
+ ![Tests](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/ff84fbfac358820cf9c9e9e582c063cd1a137116/badges/tests.svg)
44
48
  [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/rhizomatics/updates2mqtt/main.svg)](https://results.pre-commit.ci/latest/github/rhizomatics/updates2mqtt/main)
45
49
  [![Publish Python 🐍 distribution 📦 to PyPI and TestPyPI](https://github.com/rhizomatics/updates2mqtt/actions/workflows/pypi-publish.yml/badge.svg)](https://github.com/rhizomatics/updates2mqtt/actions/workflows/pypi-publish.yml)
46
50
  [![Github Deploy](https://github.com/rhizomatics/updates2mqtt/actions/workflows/python-package.yml/badge.svg?branch=main)](https://github.com/rhizomatics/updates2mqtt/actions/workflows/python-package.yml)
@@ -49,9 +53,14 @@ Description-Content-Type: text/markdown
49
53
 
50
54
  ## Summary
51
55
 
52
- Use Home Assistant to notify you of updates to Docker images for your containers and optionally perform the *pull* (or optionally *build*) and *update*.
56
+ Let Home Assistant tell you about new updates to Docker images for your containers.
57
+
58
+ ![Example Home Assistant update page](images/ha_update_detail.png "Home Assistant Updates")
59
+
60
+ Read the release notes, and optionally click *Update* to trigger a Docker *pull* (or optionally *build*) and *update*.
61
+
62
+ ![Example Home Assistant update dialog](images/ha_update_dialog.png "Home Assistant Updates"){width=480}
53
63
 
54
- ![Example Home Assistant update dialog](images/ha_update_detail.png "Home Assistant Updates")
55
64
 
56
65
  ## Description
57
66
 
@@ -59,7 +68,7 @@ updates2mqtt perioidically checks for new versions of components being available
59
68
 
60
69
  Currently only Docker containers are supported, either via an image registry check, or a git repo for source (see [Local Builds](local_builds.md)). The design is modular, so other update sources can be added, at least for notification. The next anticipated is **apt** for Debian based systems.
61
70
 
62
- Components can also be updated, either automatically or triggered via MQTT, for example by hitting the *Install* button in the HomeAssistant update dialog. Icons and release notes can be specified for a better HA experience.
71
+ Components can also be updated, either automatically or triggered via MQTT, for example by hitting the *Install* button in the HomeAssistant update dialog. Icons and release notes can be specified for a better HA experience. See [Home Assistant Integration](home_assistant.md) for details.
63
72
 
64
73
  To get started, read the [Installation](installation.md) and [Configuration](configuration.md) pages.
65
74
 
@@ -71,8 +80,7 @@ docker run -e MQTT_USER=user1 -e MQTT_PASS=pass1 -e MQTT_HOST=192.168.1.5 ghcr.i
71
80
 
72
81
  ## Release Support
73
82
 
74
- Presently only Docker containers are supported, although others are planned,
75
- probably with priority for `apt`.
83
+ Presently only Docker containers are supported, although others are planned, probably with priority for `apt`.
76
84
 
77
85
  | Ecosystem | Support | Comments |
78
86
  |-----------|-------------|----------------------------------------------------------------------------------------------------|
@@ -82,31 +90,57 @@ probably with priority for `apt`.
82
90
 
83
91
  A heartbeat JSON payload is optionally published periodically to a configurable MQTT topic, defaulting to `healthcheck/{node_name}/updates2mqtt`. It contains the current version of updates2mqtt, the node name, a timestamp, and some basic stats.
84
92
 
85
- A `healthcheck.sh` script is included in the Docker image, and can be used as a Docker healthcheck, if the container environment variables are set for `MQTT_HOST`, `MQTT_PORT`, `MQTT_USER` and `MQTT_PASS`.
93
+ A `healthcheck.sh` script is included in the Docker image, and can be used as a Docker healthcheck, if the container environment variables are set for `MQTT_HOST`, `MQTT_PORT`, `MQTT_USER` and `MQTT_PASS`. It uses the `mosquitto-clients` Linux package which provides `mosquitto_sub` command to subscribe to topics.
86
94
 
87
- TIP: Check healthcheck is working using `docker inspect --format "{{json .State.Health }}" updates2mqtt | jq`
88
-
89
- ## HomeAssistant integration
95
+ !!! tip
90
96
 
91
- Any updates that have support for automated install will automatically show in the
92
- Home Assistant settings page if the [MQTT Integration](https://www.home-assistant.io/integrations/mqtt/) is installed and automatic discovery is not disabled.
97
+ Check healthcheck is working using `docker inspect --format "{{json .State.Health }}" updates2mqtt | jq`
93
98
 
94
- ![Home Assistant MQTT Integraion configuration](images/ha_mqtt_discovery.png "Home Assistant MQTT Discovery")
99
+ Another approach is using a restarter service directly in Docker Compose to force a restart, in this case once a day:
95
100
 
96
- The `homeassistant` default topic prefix matches the default updates2mqtt config, if its changed in HomeAssistant, then the updates2mqtt config must be changed to match.
101
+ ```yaml title="Example Compose Service"
102
+ restarter:
103
+ image: docker:cli
104
+ volumes: ["/var/run/docker.sock:/var/run/docker.sock"]
105
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart updates2mqtt; done"]
106
+ restart: unless-stopped
107
+ environment:
108
+ - UPD2MQTT_UPDATE=AUTO
109
+ ```
97
110
 
98
- ![Home Assistant updates in Settings](images/ha_update_page.png "Home Assistant Updates")
111
+ ## Target Containers
99
112
 
100
- For Home Assistant integration, updates2mqtt represents each component being managed as a [MQTT Update](https://www.home-assistant.io/integrations/update.mqtt/) entity, and uses [MQTT discovery(https://www.home-assistant.io/integrations/mqtt/#mqtt-discovery)] so that HomeAssistant automatically picks up components discovered by updates2mqtt with zero configuration on HomeAssistant itself.
113
+ While `updates2mqtt` will discover and monitor all containers running under the Docker daemon,
114
+ there are some options to make to those containers to tune how it works.
101
115
 
102
- There are 3 separate types of MQTT topic used for HomeAssisstant integration:
116
+ These happen by adding environment variables to the containers, typically inside an `.env`
117
+ file, or as `environment` options inside `docker-compose.yaml`.
103
118
 
104
- - *Config* to support auto discovery. A topic is created per component, with a name like `homeassistant/update/dockernuc_docker_jellyfin/update/config`. This can be disabled in the config file, and the `homeassistant` topic prefix can also be configured.
105
- - *State* to report the current version and the latest version available, again one topic per component, like `updates2mqtt/dockernuc/docker/jellyfin`.
106
- - *Command* to support triggering an update. These will be created on the fly by HomeAssistant when an update is requested, and updates2mqtt subscribes to pick up the changes, so you won't typically see these if browsing MQTT topics. Only one is needed per updates2mqtt agent, with a name like `updates2mqtt/dockernuc/docker`
119
+ ### Automated updates
120
+
121
+ If Docker containers should be immediately updated, without any confirmation
122
+ or trigger, *e.g.* from the HomeAssistant update dialog, then set an environment variable `UPD2MQTT_UPDATE` in the target container to `Auto` ( it defaults to `Passive`)
123
+
124
+ ```yaml title="Example Compose Snippet"
125
+ restarter:
126
+ image: docker:cli
127
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart mailserver; done"]
128
+ environment:
129
+ - UPD2MQTT_UPDATE=AUTO
130
+ ```
131
+
132
+ ### Environment Variables
133
+
134
+ The following environment variables can be used to configure containers for `updates2mqtt`:
135
+
136
+ | Env Var | Description | Default |
137
+ |---------| ------------|----------|
138
+ | `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
139
+ | `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
140
+ | `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
141
+ | `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
142
+ | `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by updates2mqtt. | False |
107
143
 
108
- If the package supports automated update, then *Skip* and *Install* buttons will appear on the Home Assistant
109
- interface, and the package can be remotely fetched and the component restarted.
110
144
 
111
145
  ## Related Projects
112
146
 
@@ -114,6 +148,18 @@ Other apps useful for self-hosting with the help of MQTT:
114
148
 
115
149
  - [psmqtt](https://github.com/eschava/psmqtt) - Report system health and metrics via MQTT
116
150
 
151
+ Find more at [awesome-mqtt](https://github.com/rhizomatics/awesome-mqtt)
152
+
117
153
  ## Development
118
154
 
119
- Access to Docker APIs uses the Python [docker-py](https://docker-py.readthedocs.io/en/stable/) SDK for Python. [Eclipse Paho](https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html) is used for MQTT access, and [OmegaConf](https://omegaconf.readthedocs.io) for configuration.
155
+ This component relies on several open source packages:
156
+
157
+ - [docker-py](https://docker-py.readthedocs.io/en/stable/) SDK for Python for access to Docker APIs
158
+ - [Eclipse Paho](https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html) MQTT client
159
+ - [OmegaConf](https://omegaconf.readthedocs.io) for configuration and validation
160
+ - [structlog](https://www.structlog.org/en/stable/) for structured logging and [rich](https://rich.readthedocs.io/en/stable/) for better exception reporting
161
+ - [hishel](https://hishel.com/1.0/) for caching metadata
162
+ - [httpx](https://www.python-httpx.org) for retrieving metadata
163
+ - The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
164
+ - [pytest](https://docs.pytest.org/en/stable/) and supporting add-ins for automated testing
165
+ - [usingversion](https://pypi.org/project/usingversion/) to log current version info
@@ -0,0 +1,128 @@
1
+ [![Rhizomatics Open Source](https://avatars.githubusercontent.com/u/162821163?s=96&v=4)](https://github.com/rhizomatics)
2
+
3
+ # updates2mqtt
4
+
5
+ [![Rhizomatics Open Source](https://img.shields.io/badge/rhizomatics%20open%20source-lightseagreen)](https://github.com/rhizomatics)
6
+
7
+ [![PyPI - Version](https://img.shields.io/pypi/v/updates2mqtt)](https://pypi.org/project/updates2mqtt/)
8
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/rhizomatics/supernotify)
9
+ ![Coverage](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/ff84fbfac358820cf9c9e9e582c063cd1a137116/badges/coverage.svg)
10
+ ![Tests](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/ff84fbfac358820cf9c9e9e582c063cd1a137116/badges/tests.svg)
11
+ [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/rhizomatics/updates2mqtt/main.svg)](https://results.pre-commit.ci/latest/github/rhizomatics/updates2mqtt/main)
12
+ [![Publish Python 🐍 distribution 📦 to PyPI and TestPyPI](https://github.com/rhizomatics/updates2mqtt/actions/workflows/pypi-publish.yml/badge.svg)](https://github.com/rhizomatics/updates2mqtt/actions/workflows/pypi-publish.yml)
13
+ [![Github Deploy](https://github.com/rhizomatics/updates2mqtt/actions/workflows/python-package.yml/badge.svg?branch=main)](https://github.com/rhizomatics/updates2mqtt/actions/workflows/python-package.yml)
14
+ [![CodeQL](https://github.com/rhizomatics/updates2mqtt/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/rhizomatics/updates2mqtt/actions/workflows/github-code-scanning/codeql)
15
+ [![Dependabot Updates](https://github.com/rhizomatics/updates2mqtt/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/rhizomatics/updates2mqtt/actions/workflows/dependabot/dependabot-updates)
16
+
17
+ ## Summary
18
+
19
+ Let Home Assistant tell you about new updates to Docker images for your containers.
20
+
21
+ ![Example Home Assistant update page](images/ha_update_detail.png "Home Assistant Updates")
22
+
23
+ Read the release notes, and optionally click *Update* to trigger a Docker *pull* (or optionally *build*) and *update*.
24
+
25
+ ![Example Home Assistant update dialog](images/ha_update_dialog.png "Home Assistant Updates"){width=480}
26
+
27
+
28
+ ## Description
29
+
30
+ updates2mqtt perioidically checks for new versions of components being available, and publishes new version info to MQTT. HomeAssistant auto discovery is supported, so all updates can be seen in the same place as Home Assistant's own components and add-ins.
31
+
32
+ Currently only Docker containers are supported, either via an image registry check, or a git repo for source (see [Local Builds](local_builds.md)). The design is modular, so other update sources can be added, at least for notification. The next anticipated is **apt** for Debian based systems.
33
+
34
+ Components can also be updated, either automatically or triggered via MQTT, for example by hitting the *Install* button in the HomeAssistant update dialog. Icons and release notes can be specified for a better HA experience. See [Home Assistant Integration](home_assistant.md) for details.
35
+
36
+ To get started, read the [Installation](installation.md) and [Configuration](configuration.md) pages.
37
+
38
+ For a quick spin, try this:
39
+
40
+ ```yaml
41
+ docker run -e MQTT_USER=user1 -e MQTT_PASS=pass1 -e MQTT_HOST=192.168.1.5 ghcr.io/rhizomatics/updates2mqtt:release
42
+ ```
43
+
44
+ ## Release Support
45
+
46
+ Presently only Docker containers are supported, although others are planned, probably with priority for `apt`.
47
+
48
+ | Ecosystem | Support | Comments |
49
+ |-----------|-------------|----------------------------------------------------------------------------------------------------|
50
+ | Docker | Scan. Fetch | Fetch is ``docker pull`` only. Restart support only for ``docker-compose`` image based containers. |
51
+
52
+ ## Healthcheck
53
+
54
+ A heartbeat JSON payload is optionally published periodically to a configurable MQTT topic, defaulting to `healthcheck/{node_name}/updates2mqtt`. It contains the current version of updates2mqtt, the node name, a timestamp, and some basic stats.
55
+
56
+ A `healthcheck.sh` script is included in the Docker image, and can be used as a Docker healthcheck, if the container environment variables are set for `MQTT_HOST`, `MQTT_PORT`, `MQTT_USER` and `MQTT_PASS`. It uses the `mosquitto-clients` Linux package which provides `mosquitto_sub` command to subscribe to topics.
57
+
58
+ !!! tip
59
+
60
+ Check healthcheck is working using `docker inspect --format "{{json .State.Health }}" updates2mqtt | jq`
61
+
62
+ Another approach is using a restarter service directly in Docker Compose to force a restart, in this case once a day:
63
+
64
+ ```yaml title="Example Compose Service"
65
+ restarter:
66
+ image: docker:cli
67
+ volumes: ["/var/run/docker.sock:/var/run/docker.sock"]
68
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart updates2mqtt; done"]
69
+ restart: unless-stopped
70
+ environment:
71
+ - UPD2MQTT_UPDATE=AUTO
72
+ ```
73
+
74
+ ## Target Containers
75
+
76
+ While `updates2mqtt` will discover and monitor all containers running under the Docker daemon,
77
+ there are some options to make to those containers to tune how it works.
78
+
79
+ These happen by adding environment variables to the containers, typically inside an `.env`
80
+ file, or as `environment` options inside `docker-compose.yaml`.
81
+
82
+ ### Automated updates
83
+
84
+ If Docker containers should be immediately updated, without any confirmation
85
+ or trigger, *e.g.* from the HomeAssistant update dialog, then set an environment variable `UPD2MQTT_UPDATE` in the target container to `Auto` ( it defaults to `Passive`)
86
+
87
+ ```yaml title="Example Compose Snippet"
88
+ restarter:
89
+ image: docker:cli
90
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart mailserver; done"]
91
+ environment:
92
+ - UPD2MQTT_UPDATE=AUTO
93
+ ```
94
+
95
+ ### Environment Variables
96
+
97
+ The following environment variables can be used to configure containers for `updates2mqtt`:
98
+
99
+ | Env Var | Description | Default |
100
+ |---------| ------------|----------|
101
+ | `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
102
+ | `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
103
+ | `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
104
+ | `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
105
+ | `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by updates2mqtt. | False |
106
+
107
+
108
+ ## Related Projects
109
+
110
+ Other apps useful for self-hosting with the help of MQTT:
111
+
112
+ - [psmqtt](https://github.com/eschava/psmqtt) - Report system health and metrics via MQTT
113
+
114
+ Find more at [awesome-mqtt](https://github.com/rhizomatics/awesome-mqtt)
115
+
116
+ ## Development
117
+
118
+ This component relies on several open source packages:
119
+
120
+ - [docker-py](https://docker-py.readthedocs.io/en/stable/) SDK for Python for access to Docker APIs
121
+ - [Eclipse Paho](https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html) MQTT client
122
+ - [OmegaConf](https://omegaconf.readthedocs.io) for configuration and validation
123
+ - [structlog](https://www.structlog.org/en/stable/) for structured logging and [rich](https://rich.readthedocs.io/en/stable/) for better exception reporting
124
+ - [hishel](https://hishel.com/1.0/) for caching metadata
125
+ - [httpx](https://www.python-httpx.org) for retrieving metadata
126
+ - The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
127
+ - [pytest](https://docs.pytest.org/en/stable/) and supporting add-ins for automated testing
128
+ - [usingversion](https://pypi.org/project/usingversion/) to log current version info
@@ -1,26 +1,89 @@
1
- from unittest.mock import MagicMock, Mock, patch
1
+ # python
2
+ from collections.abc import AsyncGenerator, Callable
3
+ from typing import Any
4
+ from unittest.mock import AsyncMock, MagicMock, Mock, patch
2
5
 
3
6
  import paho.mqtt.client
4
7
  import pytest
5
8
  from docker import DockerClient
6
9
  from docker.models.containers import Container, ContainerCollection
7
10
  from docker.models.images import Image, RegistryData
11
+ from omegaconf import DictConfig, OmegaConf
8
12
 
13
+ import updates2mqtt.app
14
+ from updates2mqtt.app import (
15
+ App, # relative import as required
16
+ MqttPublisher,
17
+ )
18
+ from updates2mqtt.config import Config
9
19
  from updates2mqtt.model import Discovery, ReleaseProvider
10
20
 
11
21
 
22
+ @pytest.fixture
23
+ def app_with_mocked_external_dependencies(
24
+ monkeypatch, # noqa: ANN001
25
+ mock_provider_class: type,
26
+ mock_publisher_class: type,
27
+ ) -> App:
28
+ cfg: DictConfig = OmegaConf.structured(Config)
29
+ monkeypatch.setattr(updates2mqtt.app, "load_app_config", lambda *_args, **__kwargs: cfg)
30
+ monkeypatch.setattr(updates2mqtt.app, "DockerProvider", mock_provider_class)
31
+ monkeypatch.setattr(updates2mqtt.app, "MqttPublisher", mock_publisher_class)
32
+ app: App = App()
33
+ return app
34
+
35
+
36
+ @pytest.fixture
37
+ def mock_discoveries(mock_provider: ReleaseProvider) -> list[Discovery]:
38
+ return [Discovery(mock_provider, "thing-1", "test001")]
39
+
40
+
41
+ @pytest.fixture
42
+ def mock_discovery_generator(mock_discoveries: list[Discovery]) -> Callable[..., AsyncGenerator[Discovery, Any]]:
43
+ async def g(*args: Any) -> AsyncGenerator[Discovery]: # noqa: ARG001
44
+ for d in mock_discoveries:
45
+ yield d
46
+
47
+ return g
48
+
49
+
50
+ @pytest.fixture
51
+ def mock_provider_class(mock_provider: ReleaseProvider) -> type:
52
+ class MockReleaseProvider(ReleaseProvider):
53
+ def __new__(cls, *args: Any, **kwargs: Any) -> ReleaseProvider: # type: ignore[misc] # noqa: ARG004
54
+ return mock_provider
55
+
56
+ return MockReleaseProvider
57
+
58
+
59
+ @pytest.fixture
60
+ def mock_publisher_class(mock_publisher: MqttPublisher) -> type:
61
+ class MockPublisher(MqttPublisher):
62
+ def __new__(cls, *args: Any, **kwargs: Any) -> MqttPublisher: # type: ignore[misc] # noqa: ARG004
63
+ return mock_publisher
64
+
65
+ return MockPublisher
66
+
67
+
12
68
  @pytest.fixture
13
69
  def mock_provider() -> ReleaseProvider:
14
- provider = Mock(spec=ReleaseProvider)
70
+ provider: ReleaseProvider = AsyncMock(spec=ReleaseProvider)
15
71
  provider.source_type = "unit_test"
16
- provider.command.return_value = True
17
- provider.resolve.return_value = Discovery(
72
+ provider.command.return_value = True # type: ignore[attr-defined]
73
+ provider.resolve.return_value = Discovery( # type: ignore[attr-defined]
18
74
  provider, "fooey", session="test-mqtt-123", current_version="v2", latest_version="v2"
19
75
  )
20
- provider.hass_state_format.return_value = {"fixture": "test_exec"}
76
+ provider.hass_state_format.return_value = {"fixture": "test_exec"} # type: ignore[attr-defined]
21
77
  return provider
22
78
 
23
79
 
80
+ @pytest.fixture
81
+ def mock_publisher(mock_mqtt_client: paho.mqtt.client.Client) -> MqttPublisher:
82
+ publisher: MqttPublisher = AsyncMock(MqttPublisher)
83
+ publisher.client = mock_mqtt_client
84
+ return publisher
85
+
86
+
24
87
  @pytest.fixture
25
88
  def mock_mqtt_client() -> paho.mqtt.client.Client:
26
89
  return MagicMock(spec=paho.mqtt.client.Client, name="MQTT Client Fixture")