updates2mqtt 1.5.0__tar.gz → 1.6.0__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 (68) hide show
  1. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/PKG-INFO +71 -32
  2. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/README.md +52 -13
  3. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/pyproject.toml +8 -6
  4. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/app.py +15 -6
  5. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/config.py +14 -6
  6. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/hass_formatter.py +8 -17
  7. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/integrations/docker.py +210 -46
  8. updates2mqtt-1.6.0/src/updates2mqtt/integrations/git_utils.py +123 -0
  9. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/model.py +27 -1
  10. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/mqtt.py +2 -4
  11. updates2mqtt-1.5.0/.dockerignore +0 -13
  12. updates2mqtt-1.5.0/.github/dependabot.yml +0 -30
  13. updates2mqtt-1.5.0/.github/workflows/auto_assign_issue.yml +0 -18
  14. updates2mqtt-1.5.0/.github/workflows/auto_assign_pr.yml +0 -18
  15. updates2mqtt-1.5.0/.github/workflows/docker-publish.yml +0 -78
  16. updates2mqtt-1.5.0/.github/workflows/main.yml +0 -18
  17. updates2mqtt-1.5.0/.github/workflows/pypi-publish.yml +0 -78
  18. updates2mqtt-1.5.0/.github/workflows/python-package.yml +0 -67
  19. updates2mqtt-1.5.0/.gitignore +0 -171
  20. updates2mqtt-1.5.0/.hintrc +0 -8
  21. updates2mqtt-1.5.0/.pre-commit-config.yaml +0 -71
  22. updates2mqtt-1.5.0/.python-version +0 -1
  23. updates2mqtt-1.5.0/.safety-project.ini +0 -5
  24. updates2mqtt-1.5.0/CHANGELOG.md +0 -36
  25. updates2mqtt-1.5.0/Dockerfile +0 -39
  26. updates2mqtt-1.5.0/LICENSE +0 -201
  27. updates2mqtt-1.5.0/SECURITY.md +0 -29
  28. updates2mqtt-1.5.0/common_packages.yaml +0 -119
  29. updates2mqtt-1.5.0/conftest.py +0 -147
  30. updates2mqtt-1.5.0/docs/configuration.md +0 -129
  31. updates2mqtt-1.5.0/docs/examples/config_maximal.md +0 -7
  32. updates2mqtt-1.5.0/docs/examples/config_minimal.md +0 -7
  33. updates2mqtt-1.5.0/docs/examples/docker_compose.md +0 -9
  34. updates2mqtt-1.5.0/docs/examples/env.md +0 -7
  35. updates2mqtt-1.5.0/docs/examples/index.md +0 -5
  36. updates2mqtt-1.5.0/docs/home_assistant.md +0 -51
  37. updates2mqtt-1.5.0/docs/images/ha_entities.png +0 -0
  38. updates2mqtt-1.5.0/docs/images/ha_mqtt_discovery.png +0 -0
  39. updates2mqtt-1.5.0/docs/images/ha_update_detail.png +0 -0
  40. updates2mqtt-1.5.0/docs/images/ha_update_dialog.png +0 -0
  41. updates2mqtt-1.5.0/docs/images/ha_update_page.png +0 -0
  42. updates2mqtt-1.5.0/docs/images/logo-blank-256x256.png +0 -0
  43. updates2mqtt-1.5.0/docs/images/updates2mqtt-dark-256x256.png +0 -0
  44. updates2mqtt-1.5.0/docs/index.md +0 -1
  45. updates2mqtt-1.5.0/docs/installation.md +0 -58
  46. updates2mqtt-1.5.0/docs/local_builds.md +0 -22
  47. updates2mqtt-1.5.0/docs/robots.txt +0 -4
  48. updates2mqtt-1.5.0/docs/troubleshooting.md +0 -171
  49. updates2mqtt-1.5.0/examples/config.yaml.maximal +0 -37
  50. updates2mqtt-1.5.0/examples/config.yaml.minimal +0 -9
  51. updates2mqtt-1.5.0/examples/docker-compose.yaml +0 -18
  52. updates2mqtt-1.5.0/mkdocs.yml +0 -81
  53. updates2mqtt-1.5.0/no_config.yaml +0 -33
  54. updates2mqtt-1.5.0/refresh-deps.sh +0 -7
  55. updates2mqtt-1.5.0/scripts/healthcheck.sh +0 -91
  56. updates2mqtt-1.5.0/src/updates2mqtt/integrations/git_utils.py +0 -66
  57. updates2mqtt-1.5.0/tests/__init__.py +0 -0
  58. updates2mqtt-1.5.0/tests/test_app.py +0 -130
  59. updates2mqtt-1.5.0/tests/test_config.py +0 -69
  60. updates2mqtt-1.5.0/tests/test_docker.py +0 -87
  61. updates2mqtt-1.5.0/tests/test_git_utils.py +0 -49
  62. updates2mqtt-1.5.0/tests/test_hass_formatter.py +0 -85
  63. updates2mqtt-1.5.0/tests/test_mqtt.py +0 -139
  64. updates2mqtt-1.5.0/uv.lock +0 -1454
  65. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/__init__.py +0 -0
  66. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/__main__.py +0 -0
  67. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/integrations/__init__.py +0 -0
  68. {updates2mqtt-1.5.0 → updates2mqtt-1.6.0}/src/updates2mqtt/py.typed +0 -0
@@ -1,38 +1,38 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: updates2mqtt
3
- Version: 1.5.0
3
+ Version: 1.6.0
4
4
  Summary: System update and docker image notification and execution over MQTT
5
- Project-URL: Homepage, https://updates2mqtt.rhizomatics.org.uk
6
- Project-URL: Repository, https://github.com/rhizomatics/updates2mqtt
7
- Project-URL: Documentation, https://updates2mqtt.rhizomatics.org.uk
8
- Project-URL: Issues, https://github.com/rhizomatics/updates2mqtt/issues
9
- Project-URL: Changelog, https://github.com/rhizomatics/updates2mqtt/blob/main/CHANGELOG.md
5
+ Keywords: mqtt,docker,updates,automation,home-assistant,homeassistant,selfhosting
6
+ Author: jey burrows
10
7
  Author-email: jey burrows <jrb@rhizomatics.org.uk>
11
8
  License-Expression: Apache-2.0
12
- License-File: LICENSE
13
- Keywords: automation,docker,home-assistant,homeassistant,mqtt,selfhosting,updates
14
9
  Classifier: Development Status :: 5 - Production/Stable
15
- Classifier: Environment :: Console
16
- Classifier: Intended Audience :: System Administrators
17
- Classifier: License :: OSI Approved :: Apache Software License
18
10
  Classifier: License :: Other/Proprietary License
19
11
  Classifier: Natural Language :: English
20
12
  Classifier: Operating System :: OS Independent
21
- Classifier: Programming Language :: Python
22
- Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Environment :: Console
23
14
  Classifier: Topic :: Home Automation
24
- Classifier: Topic :: System :: Monitoring
25
15
  Classifier: Topic :: System :: Systems Administration
16
+ Classifier: Topic :: System :: Monitoring
17
+ Classifier: Intended Audience :: System Administrators
18
+ Classifier: License :: OSI Approved :: Apache Software License
26
19
  Classifier: Typing :: Typed
27
- Requires-Python: >=3.13
20
+ Classifier: Programming Language :: Python
21
+ Classifier: Programming Language :: Python :: 3.13
28
22
  Requires-Dist: docker>=7.1.0
29
- Requires-Dist: hishel[httpx]>=0.1.4
30
- Requires-Dist: httpx>=0.28.1
31
- Requires-Dist: omegaconf>=2.3.0
32
23
  Requires-Dist: paho-mqtt>=2.1.0
33
- Requires-Dist: rich>=14.0.0
24
+ Requires-Dist: omegaconf>=2.3.0
34
25
  Requires-Dist: structlog>=25.4.0
26
+ Requires-Dist: rich>=14.0.0
27
+ Requires-Dist: httpx>=0.28.1
28
+ Requires-Dist: hishel[httpx]>=0.1.4
35
29
  Requires-Dist: usingversion>=0.1.2
30
+ Requires-Python: >=3.13
31
+ Project-URL: Changelog, https://github.com/rhizomatics/updates2mqtt/blob/main/CHANGELOG.md
32
+ Project-URL: Documentation, https://updates2mqtt.rhizomatics.org.uk
33
+ Project-URL: Homepage, https://updates2mqtt.rhizomatics.org.uk
34
+ Project-URL: Issues, https://github.com/rhizomatics/updates2mqtt/issues
35
+ Project-URL: Repository, https://github.com/rhizomatics/updates2mqtt
36
36
  Description-Content-Type: text/markdown
37
37
 
38
38
  ![updates2mqtt](images/updates2mqtt-dark-256x256.png){ align=left }
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
42
42
  [![Rhizomatics Open Source](https://img.shields.io/badge/rhizomatics%20open%20source-lightseagreen)](https://github.com/rhizomatics)
43
43
 
44
44
  [![PyPI - Version](https://img.shields.io/pypi/v/updates2mqtt)](https://pypi.org/project/updates2mqtt/)
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)
45
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/rhizomatics/updates2mqtt)
46
46
  [![Coverage](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/refs/heads/badges/badges/coverage.svg)](https://updates2mqtt.rhizomatics.org.uk/developer/coverage/)
47
47
  ![Tests](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/refs/heads/badges/badges/tests.svg)
48
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)
@@ -69,7 +69,7 @@ Read the release notes, and optionally click *Update* to trigger a Docker *pull*
69
69
 
70
70
  ## Description
71
71
 
72
- 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.
72
+ 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.
73
73
 
74
74
  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.
75
75
 
@@ -79,8 +79,14 @@ To get started, read the [Installation](installation.md) and [Configuration](con
79
79
 
80
80
  For a quick spin, try this:
81
81
 
82
- ```yaml
83
- docker run -e MQTT_USER=user1 -e MQTT_PASS=pass1 -e MQTT_HOST=192.168.1.5 ghcr.io/rhizomatics/updates2mqtt:release
82
+ ```bash
83
+ docker run -v /var/run/docker.sock:/var/run/docker.sock -e MQTT_USER=user1 -e MQTT_PASS=user1 -e MQTT_HOST=192.168.1.5 ghcr.io/rhizomatics/updates2mqtt:release
84
+ ```
85
+
86
+ or without Docker, using [uv](https://docs.astral.sh/uv/)
87
+
88
+ ```bash
89
+ export MQTT_HOST=192.168.1.1;export MQTT_USER=user1;export MQTT_PASS=user1;uv run --with updates2mqtt python -m updates2mqtt
84
90
  ```
85
91
 
86
92
  ## Release Support
@@ -93,7 +99,7 @@ Presently only Docker containers are supported, although others are planned, pro
93
99
 
94
100
  ## Heartbeat
95
101
 
96
- 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.
102
+ 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.
97
103
 
98
104
  ## Healthcheck
99
105
 
@@ -136,17 +142,48 @@ restarter:
136
142
  - UPD2MQTT_UPDATE=AUTO
137
143
  ```
138
144
 
145
+ This can be used in conjunction with the `UPD2MQTT_VERSION_INCLUDE` and `UPD2MQTT_VERSION_EXCLUDE` to
146
+ limit which updates get automatically applied, for example excluding nightly builds.
147
+
148
+ Automated updates can also apply to local builds, where a `git_repo_path` has been defined - if there are remote
149
+ commits available to pull, then a `git pull`, `docker compose build` and `docker compose up` will be executed.
150
+
139
151
  ### Environment Variables
140
152
 
141
153
  The following environment variables can be used to configure containers for `updates2mqtt`:
142
154
 
143
- | Env Var | Description | Default |
144
- |---------| ------------|----------|
145
- | `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
146
- | `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
147
- | `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
148
- | `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
149
- | `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by updates2mqtt. | False |
155
+ | Env Var | Description | Default |
156
+ |----------------------------|----------------------------------------------------------------------------------------------|-----------------|
157
+ | `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
158
+ | `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
159
+ | `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
160
+ | `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
161
+ | `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by Updates2MQTT. | False |
162
+ | `UPD2MQTT_VERSION_INCLUDE` | Only recognize versions matching this string or regular expression | |
163
+ | `UPD2MQTT_VERSION_EXCLUDE` | Skip update if version matches this string or regular expression | |
164
+
165
+ ### Docker Labels
166
+
167
+ Alternatively, use Docker labels
168
+
169
+ | Label | Env Var |
170
+ |--------------------------------|----------------------------|
171
+ | `updates2mqtt.update` | `UPD2MQTT_UPDATE` |
172
+ | `updates2mqtt.picture` | `UPD2MQTT_PCITURE` |
173
+ | `updates2mqtt.relnotes` | `UPD2MQTT_RELNOTES` |
174
+ | `updates2mqtt.git_repo_path` | `UPD2MQTT_GIT_REPO_PATH` |
175
+ | `updates2mqtt.ignore` | `UPD2MQTT_IGNORE` |
176
+ | `updates2mqtt.version_include` | `UPD2MQTT_VERSION_INCLUDE` |
177
+ | `updates2mqtt.version_exclude` | `UPD2MQTT_VERSION_EXCLUDE` |
178
+
179
+
180
+ ```yaml title="Example Compose Snippet"
181
+ restarter:
182
+ image: docker:cli
183
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart mailserver; done"]
184
+ labels:
185
+ updates2mqtt.relnotes: https://component.my.com/release_notes
186
+ ```
150
187
 
151
188
 
152
189
  ## Related Projects
@@ -157,6 +194,8 @@ Other apps useful for self-hosting with the help of MQTT:
157
194
 
158
195
  Find more at [awesome-mqtt](https://github.com/rhizomatics/awesome-mqtt)
159
196
 
197
+ For a more powerful Docker update manager, try [What's Up Docker](https://getwud.github.io/wud/)
198
+
160
199
  ## Development
161
200
 
162
201
  This component relies on several open source packages:
@@ -169,4 +208,4 @@ This component relies on several open source packages:
169
208
  - [httpx](https://www.python-httpx.org) for retrieving metadata
170
209
  - The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
171
210
  - [pytest](https://docs.pytest.org/en/stable/) and supporting add-ins for automated testing
172
- - [usingversion](https://pypi.org/project/usingversion/) to log current version info
211
+ - [usingversion](https://pypi.org/project/usingversion/) to log current version info
@@ -5,7 +5,7 @@
5
5
  [![Rhizomatics Open Source](https://img.shields.io/badge/rhizomatics%20open%20source-lightseagreen)](https://github.com/rhizomatics)
6
6
 
7
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)
8
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/rhizomatics/updates2mqtt)
9
9
  [![Coverage](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/refs/heads/badges/badges/coverage.svg)](https://updates2mqtt.rhizomatics.org.uk/developer/coverage/)
10
10
  ![Tests](https://raw.githubusercontent.com/rhizomatics/updates2mqtt/refs/heads/badges/badges/tests.svg)
11
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)
@@ -32,7 +32,7 @@ Read the release notes, and optionally click *Update* to trigger a Docker *pull*
32
32
 
33
33
  ## Description
34
34
 
35
- 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.
35
+ 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.
36
36
 
37
37
  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.
38
38
 
@@ -42,8 +42,14 @@ To get started, read the [Installation](installation.md) and [Configuration](con
42
42
 
43
43
  For a quick spin, try this:
44
44
 
45
- ```yaml
46
- docker run -e MQTT_USER=user1 -e MQTT_PASS=pass1 -e MQTT_HOST=192.168.1.5 ghcr.io/rhizomatics/updates2mqtt:release
45
+ ```bash
46
+ docker run -v /var/run/docker.sock:/var/run/docker.sock -e MQTT_USER=user1 -e MQTT_PASS=user1 -e MQTT_HOST=192.168.1.5 ghcr.io/rhizomatics/updates2mqtt:release
47
+ ```
48
+
49
+ or without Docker, using [uv](https://docs.astral.sh/uv/)
50
+
51
+ ```bash
52
+ export MQTT_HOST=192.168.1.1;export MQTT_USER=user1;export MQTT_PASS=user1;uv run --with updates2mqtt python -m updates2mqtt
47
53
  ```
48
54
 
49
55
  ## Release Support
@@ -56,7 +62,7 @@ Presently only Docker containers are supported, although others are planned, pro
56
62
 
57
63
  ## Heartbeat
58
64
 
59
- 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.
65
+ 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.
60
66
 
61
67
  ## Healthcheck
62
68
 
@@ -99,17 +105,48 @@ restarter:
99
105
  - UPD2MQTT_UPDATE=AUTO
100
106
  ```
101
107
 
108
+ This can be used in conjunction with the `UPD2MQTT_VERSION_INCLUDE` and `UPD2MQTT_VERSION_EXCLUDE` to
109
+ limit which updates get automatically applied, for example excluding nightly builds.
110
+
111
+ Automated updates can also apply to local builds, where a `git_repo_path` has been defined - if there are remote
112
+ commits available to pull, then a `git pull`, `docker compose build` and `docker compose up` will be executed.
113
+
102
114
  ### Environment Variables
103
115
 
104
116
  The following environment variables can be used to configure containers for `updates2mqtt`:
105
117
 
106
- | Env Var | Description | Default |
107
- |---------| ------------|----------|
108
- | `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
109
- | `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
110
- | `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
111
- | `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
112
- | `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by updates2mqtt. | False |
118
+ | Env Var | Description | Default |
119
+ |----------------------------|----------------------------------------------------------------------------------------------|-----------------|
120
+ | `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
121
+ | `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
122
+ | `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
123
+ | `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
124
+ | `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by Updates2MQTT. | False |
125
+ | `UPD2MQTT_VERSION_INCLUDE` | Only recognize versions matching this string or regular expression | |
126
+ | `UPD2MQTT_VERSION_EXCLUDE` | Skip update if version matches this string or regular expression | |
127
+
128
+ ### Docker Labels
129
+
130
+ Alternatively, use Docker labels
131
+
132
+ | Label | Env Var |
133
+ |--------------------------------|----------------------------|
134
+ | `updates2mqtt.update` | `UPD2MQTT_UPDATE` |
135
+ | `updates2mqtt.picture` | `UPD2MQTT_PCITURE` |
136
+ | `updates2mqtt.relnotes` | `UPD2MQTT_RELNOTES` |
137
+ | `updates2mqtt.git_repo_path` | `UPD2MQTT_GIT_REPO_PATH` |
138
+ | `updates2mqtt.ignore` | `UPD2MQTT_IGNORE` |
139
+ | `updates2mqtt.version_include` | `UPD2MQTT_VERSION_INCLUDE` |
140
+ | `updates2mqtt.version_exclude` | `UPD2MQTT_VERSION_EXCLUDE` |
141
+
142
+
143
+ ```yaml title="Example Compose Snippet"
144
+ restarter:
145
+ image: docker:cli
146
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart mailserver; done"]
147
+ labels:
148
+ updates2mqtt.relnotes: https://component.my.com/release_notes
149
+ ```
113
150
 
114
151
 
115
152
  ## Related Projects
@@ -120,6 +157,8 @@ Other apps useful for self-hosting with the help of MQTT:
120
157
 
121
158
  Find more at [awesome-mqtt](https://github.com/rhizomatics/awesome-mqtt)
122
159
 
160
+ For a more powerful Docker update manager, try [What's Up Docker](https://getwud.github.io/wud/)
161
+
123
162
  ## Development
124
163
 
125
164
  This component relies on several open source packages:
@@ -132,4 +171,4 @@ This component relies on several open source packages:
132
171
  - [httpx](https://www.python-httpx.org) for retrieving metadata
133
172
  - The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
134
173
  - [pytest](https://docs.pytest.org/en/stable/) and supporting add-ins for automated testing
135
- - [usingversion](https://pypi.org/project/usingversion/) to log current version info
174
+ - [usingversion](https://pypi.org/project/usingversion/) to log current version info
@@ -7,7 +7,7 @@ authors = [
7
7
  ]
8
8
 
9
9
  requires-python = ">=3.13"
10
- version = "1.5.0"
10
+ version = "1.6.0"
11
11
  license="Apache-2.0"
12
12
  keywords=["mqtt", "docker", "updates", "automation","home-assistant","homeassistant","selfhosting"]
13
13
 
@@ -59,7 +59,7 @@ dev = [
59
59
  "icdiff",
60
60
  "genbadge[all]"
61
61
  ]
62
- mkdocs=[
62
+ docs=[
63
63
  "mkdocs",
64
64
  "mkdocs-material",
65
65
  "mkdocs-minify-plugin",
@@ -74,10 +74,12 @@ mkdocs=[
74
74
  ]
75
75
 
76
76
  [build-system]
77
- build-backend = "hatchling.build"
78
- requires=[
79
- "hatchling"
80
- ]
77
+ requires = ["uv_build>=0.9.18,<0.10.0"]
78
+ build-backend = "uv_build"
79
+
80
+ [tool.uv.build-backend]
81
+ module-root = "src"
82
+ module-name = "updates2mqtt"
81
83
 
82
84
  [tool.bandit]
83
85
  exclude_dirs = ["tests"]
@@ -39,10 +39,13 @@ class App:
39
39
  self.last_scan_timestamp: str | None = None
40
40
  app_config: Config | None = load_app_config(CONF_FILE)
41
41
  if app_config is None:
42
- log.error(f"Invalid configuration at {CONF_FILE}, edit config to fix missing or invalid values and restart")
42
+ log.error(f"Invalid configuration at {CONF_FILE}")
43
+ log.error("Edit config to fix missing or invalid values and restart")
44
+ log.error("Alternately supply correct MQTT_HOST,MQTT_USER,MQTT_PASSWORD environment variables")
43
45
  log.error("Exiting app")
44
46
  sys.exit(1)
45
47
  self.cfg: Config = app_config
48
+ self.self_bounce: Event = Event()
46
49
 
47
50
  structlog.configure(wrapper_class=structlog.make_filtering_bound_logger(getattr(logging, str(self.cfg.log.level))))
48
51
  log.debug("Logging initialized", level=self.cfg.log.level)
@@ -54,7 +57,7 @@ class App:
54
57
  self.scan_count: int = 0
55
58
  self.last_scan: str | None = None
56
59
  if self.cfg.docker.enabled:
57
- self.scanners.append(DockerProvider(self.cfg.docker, self.common_pkg, self.cfg.node))
60
+ self.scanners.append(DockerProvider(self.cfg.docker, self.common_pkg, self.cfg.node, self.self_bounce))
58
61
  self.stopped = Event()
59
62
  self.healthcheck_topic = self.cfg.node.healthcheck.topic_template.format(node_name=self.cfg.node.name)
60
63
 
@@ -123,7 +126,11 @@ class App:
123
126
  self.publisher.publish_hass_config(discovery)
124
127
 
125
128
  self.publisher.publish_hass_state(discovery)
126
- if discovery.update_policy == "Auto":
129
+ if (
130
+ discovery.update_policy == "Auto"
131
+ and discovery.can_update
132
+ and discovery.latest_version != discovery.current_version
133
+ ):
127
134
  # TODO: review auto update, trigger by version, use update interval as throttle
128
135
  elapsed: float = (
129
136
  time.time() - discovery.update_last_attempt if discovery.update_last_attempt is not None else -1
@@ -155,7 +162,11 @@ class App:
155
162
  log.debug("Cancellation task completed")
156
163
 
157
164
  def shutdown(self, *args, exit_code: int = 143) -> None: # noqa: ANN002, ARG002
158
- log.info("Shutting down, exit_code: %s", exit_code)
165
+ if self.self_bounce.is_set():
166
+ exit_code = 1
167
+ log.info("Self bouncing, overriding exit_code: %s", exit_code)
168
+ else:
169
+ log.info("Shutting down, exit_code: %s", exit_code)
159
170
  self.stopped.set()
160
171
  for scanner in self.scanners:
161
172
  scanner.stop()
@@ -206,8 +217,6 @@ def run() -> None:
206
217
  import asyncio
207
218
  import signal
208
219
 
209
- from .app import App
210
-
211
220
  # pyright: ignore[reportAttributeAccessIssue]
212
221
  log.debug(f"Starting updates2mqtt v{updates2mqtt.version}") # pyright: ignore[reportAttributeAccessIssue]
213
222
  app = App()
@@ -25,7 +25,7 @@ class MqttConfig:
25
25
  password: str = f"${{oc.env:MQTT_PASS,{MISSING}}}"
26
26
  port: int = "${oc.decode:${oc.env:MQTT_PORT,1883}}" # type: ignore[assignment]
27
27
  topic_root: str = "updates2mqtt"
28
- protocol: str = "3.11"
28
+ protocol: str = "${oc.env:MQTT_VERSION,3.11}"
29
29
 
30
30
 
31
31
  @dataclass
@@ -47,6 +47,7 @@ class DockerConfig:
47
47
  discover_metadata: dict[str, MetadataSourceConfig] = field(
48
48
  default_factory=lambda: {"linuxserver.io": MetadataSourceConfig(enabled=True)}
49
49
  )
50
+ api_throttle_wait: int = 60 * 15
50
51
 
51
52
 
52
53
  @dataclass
@@ -80,12 +81,12 @@ class NodeConfig:
80
81
 
81
82
  @dataclass
82
83
  class LogConfig:
83
- level: LogLevel = LogLevel.INFO
84
+ level: LogLevel = "${oc.decode:${oc.env:U2M_LOG_LEVEL,INFO}}" # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
84
85
 
85
86
 
86
87
  @dataclass
87
88
  class Config:
88
- log: LogConfig = field(default_factory=LogConfig)
89
+ log: LogConfig = field(default_factory=LogConfig) # pyright: ignore[reportArgumentType, reportCallIssue]
89
90
  node: NodeConfig = field(default_factory=NodeConfig)
90
91
  mqtt: MqttConfig = field(default_factory=MqttConfig) # pyright: ignore[reportArgumentType, reportCallIssue]
91
92
  homeassistant: HomeAssistantConfig = field(default_factory=HomeAssistantConfig)
@@ -132,22 +133,29 @@ def load_package_info(pkginfo_file_path: Path) -> dict[str, PackageUpdateInfo]:
132
133
  raise
133
134
 
134
135
 
136
+ def is_autogen_config() -> bool:
137
+ env_var: str | None = os.environ.get("U2M_AUTOGEN_CONFIG")
138
+ return not (env_var and env_var.lower() in ("no", "0", "false"))
139
+
140
+
135
141
  def load_app_config(conf_file_path: Path, return_invalid: bool = False) -> Config | None:
136
142
  base_cfg: DictConfig = OmegaConf.structured(Config)
137
143
  if conf_file_path.exists():
138
144
  cfg: DictConfig = typing.cast("DictConfig", OmegaConf.merge(base_cfg, OmegaConf.load(conf_file_path)))
139
- else:
145
+ elif is_autogen_config():
140
146
  if not conf_file_path.parent.exists():
141
147
  try:
142
148
  log.debug(f"Creating config directory {conf_file_path.parent} if not already present")
143
149
  conf_file_path.parent.mkdir(parents=True, exist_ok=True)
144
150
  except Exception:
145
- log.exception("Unable to create config directory", path=conf_file_path.parent)
151
+ log.warning("Unable to create config directory", path=conf_file_path.parent)
146
152
  try:
147
153
  conf_file_path.write_text(OmegaConf.to_yaml(base_cfg))
148
154
  log.info(f"Auto-generated a new config file at {conf_file_path}")
149
155
  except Exception:
150
- log.exception("Unable to write config file", path=conf_file_path)
156
+ log.warning("Unable to write config file", path=conf_file_path)
157
+ cfg = base_cfg
158
+ else:
151
159
  cfg = base_cfg
152
160
 
153
161
  try:
@@ -21,7 +21,6 @@ HASS_UPDATE_SCHEMA = [
21
21
  def hass_format_config(
22
22
  discovery: Discovery,
23
23
  object_id: str,
24
- node_name: str,
25
24
  state_topic: str,
26
25
  command_topic: str | None,
27
26
  force_command_topic: bool | None,
@@ -29,13 +28,8 @@ def hass_format_config(
29
28
  area: str | None = None,
30
29
  session: str | None = None,
31
30
  ) -> dict[str, Any]:
32
- if device_creation:
33
- # avoid duplication, since Home Assistant will concatenate device and entity name on update
34
- name: str = f"{discovery.name} {discovery.source_type}"
35
- else:
36
- name = f"{discovery.name} {discovery.source_type} on {node_name}"
37
31
  config: dict[str, Any] = {
38
- "name": name,
32
+ "name": discovery.title,
39
33
  "device_class": None, # not firmware, so defaults to null
40
34
  "unique_id": object_id,
41
35
  "state_topic": state_topic,
@@ -46,7 +40,7 @@ def hass_format_config(
46
40
  "can_restart": discovery.can_restart,
47
41
  "update_policy": discovery.update_policy,
48
42
  "origin": {
49
- "name": f"{node_name} updates2mqtt",
43
+ "name": f"{discovery.node} updates2mqtt",
50
44
  "sw_version": updates2mqtt.version, # pyright: ignore[reportAttributeAccessIssue]
51
45
  "support_url": "https://github.com/rhizomatics/updates2mqtt/issues",
52
46
  },
@@ -57,10 +51,10 @@ def hass_format_config(
57
51
  config["icon"] = discovery.device_icon
58
52
  if device_creation:
59
53
  config["device"] = {
60
- "name": f"{node_name} updates2mqtt",
54
+ "name": f"{discovery.node} updates2mqtt",
61
55
  "sw_version": updates2mqtt.version, # pyright: ignore[reportAttributeAccessIssue]
62
56
  "manufacturer": "rhizomatics",
63
- "identifiers": [f"{node_name}.updates2mqtt"],
57
+ "identifiers": [f"{discovery.node}.updates2mqtt"],
64
58
  }
65
59
  if area:
66
60
  config["device"]["suggested_area"] = area
@@ -68,20 +62,17 @@ def hass_format_config(
68
62
  config["command_topic"] = command_topic
69
63
  if discovery.can_update:
70
64
  config["payload_install"] = f"{discovery.source_type}|{discovery.name}|install"
71
- if discovery.custom.get("git_repo_path"):
72
- config["git_repo_path"] = discovery.custom["git_repo_path"]
65
+ config["custom"] = {}
66
+ config["custom"][discovery.source_type] = discovery.custom
73
67
  config.update(discovery.provider.hass_config_format(discovery))
74
68
  return config
75
69
 
76
70
 
77
- def hass_format_state(discovery: Discovery, node_name: str, session: str, in_progress: bool = False) -> dict[str, Any]: # noqa: ARG001
78
- title: str = (
79
- discovery.title_template.format(name=discovery.name, node=node_name) if discovery.title_template else discovery.name
80
- )
71
+ def hass_format_state(discovery: Discovery, session: str, in_progress: bool = False) -> dict[str, Any]: # noqa: ARG001
81
72
  state = {
82
73
  "installed_version": discovery.current_version,
83
74
  "latest_version": discovery.latest_version,
84
- "title": title,
75
+ "title": discovery.title,
85
76
  "in_progress": in_progress,
86
77
  }
87
78
  if discovery.release_summary: