updates2mqtt 1.5.1__tar.gz → 1.7.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.
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/PKG-INFO +68 -33
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/README.md +48 -14
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/pyproject.toml +13 -10
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/app.py +23 -11
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/config.py +30 -18
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/hass_formatter.py +7 -17
- updates2mqtt-1.7.0/src/updates2mqtt/integrations/docker.py +606 -0
- updates2mqtt-1.7.0/src/updates2mqtt/integrations/docker_enrich.py +344 -0
- updates2mqtt-1.7.0/src/updates2mqtt/integrations/git_utils.py +123 -0
- updates2mqtt-1.7.0/src/updates2mqtt/model.py +246 -0
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/mqtt.py +28 -7
- updates2mqtt-1.5.1/.dockerignore +0 -13
- updates2mqtt-1.5.1/.github/dependabot.yml +0 -30
- updates2mqtt-1.5.1/.github/workflows/auto_assign_issue.yml +0 -18
- updates2mqtt-1.5.1/.github/workflows/auto_assign_pr.yml +0 -18
- updates2mqtt-1.5.1/.github/workflows/docker-publish.yml +0 -78
- updates2mqtt-1.5.1/.github/workflows/main.yml +0 -18
- updates2mqtt-1.5.1/.github/workflows/pypi-publish.yml +0 -78
- updates2mqtt-1.5.1/.github/workflows/python-package.yml +0 -67
- updates2mqtt-1.5.1/.gitignore +0 -171
- updates2mqtt-1.5.1/.hintrc +0 -8
- updates2mqtt-1.5.1/.pre-commit-config.yaml +0 -71
- updates2mqtt-1.5.1/.python-version +0 -1
- updates2mqtt-1.5.1/.safety-project.ini +0 -5
- updates2mqtt-1.5.1/CHANGELOG.md +0 -42
- updates2mqtt-1.5.1/Dockerfile +0 -39
- updates2mqtt-1.5.1/LICENSE +0 -201
- updates2mqtt-1.5.1/SECURITY.md +0 -29
- updates2mqtt-1.5.1/common_packages.yaml +0 -119
- updates2mqtt-1.5.1/conftest.py +0 -147
- updates2mqtt-1.5.1/docs/configuration.md +0 -149
- updates2mqtt-1.5.1/docs/examples/config_maximal.md +0 -7
- updates2mqtt-1.5.1/docs/examples/config_minimal.md +0 -7
- updates2mqtt-1.5.1/docs/examples/docker_compose.md +0 -9
- updates2mqtt-1.5.1/docs/examples/env.md +0 -15
- updates2mqtt-1.5.1/docs/examples/index.md +0 -5
- updates2mqtt-1.5.1/docs/home_assistant.md +0 -51
- updates2mqtt-1.5.1/docs/images/ha_entities.png +0 -0
- updates2mqtt-1.5.1/docs/images/ha_mqtt_discovery.png +0 -0
- updates2mqtt-1.5.1/docs/images/ha_update_detail.png +0 -0
- updates2mqtt-1.5.1/docs/images/ha_update_dialog.png +0 -0
- updates2mqtt-1.5.1/docs/images/ha_update_page.png +0 -0
- updates2mqtt-1.5.1/docs/images/logo-blank-256x256.png +0 -0
- updates2mqtt-1.5.1/docs/images/updates2mqtt-dark-256x256.png +0 -0
- updates2mqtt-1.5.1/docs/index.md +0 -1
- updates2mqtt-1.5.1/docs/installation.md +0 -58
- updates2mqtt-1.5.1/docs/local_builds.md +0 -22
- updates2mqtt-1.5.1/docs/robots.txt +0 -4
- updates2mqtt-1.5.1/docs/troubleshooting.md +0 -171
- updates2mqtt-1.5.1/examples/config.yaml.maximal +0 -37
- updates2mqtt-1.5.1/examples/config.yaml.minimal +0 -9
- updates2mqtt-1.5.1/examples/docker-compose.yaml +0 -18
- updates2mqtt-1.5.1/mkdocs.yml +0 -81
- updates2mqtt-1.5.1/refresh-deps.sh +0 -7
- updates2mqtt-1.5.1/scripts/healthcheck.sh +0 -91
- updates2mqtt-1.5.1/src/updates2mqtt/integrations/docker.py +0 -454
- updates2mqtt-1.5.1/src/updates2mqtt/integrations/git_utils.py +0 -66
- updates2mqtt-1.5.1/src/updates2mqtt/model.py +0 -112
- updates2mqtt-1.5.1/tests/__init__.py +0 -0
- updates2mqtt-1.5.1/tests/test_app.py +0 -130
- updates2mqtt-1.5.1/tests/test_config.py +0 -99
- updates2mqtt-1.5.1/tests/test_docker.py +0 -87
- updates2mqtt-1.5.1/tests/test_git_utils.py +0 -49
- updates2mqtt-1.5.1/tests/test_hass_formatter.py +0 -82
- updates2mqtt-1.5.1/tests/test_mqtt.py +0 -143
- updates2mqtt-1.5.1/uv.lock +0 -1454
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/__init__.py +0 -0
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/__main__.py +0 -0
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/integrations/__init__.py +0 -0
- {updates2mqtt-1.5.1 → updates2mqtt-1.7.0}/src/updates2mqtt/py.typed +0 -0
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: updates2mqtt
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: System update and docker image notification and execution over MQTT
|
|
5
|
-
|
|
6
|
-
|
|
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,oci,container,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:
|
|
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
|
-
|
|
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:
|
|
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-Dist: tzlocal>=5.3.1
|
|
31
|
+
Requires-Python: >=3.13
|
|
32
|
+
Project-URL: Homepage, https://updates2mqtt.rhizomatics.org.uk
|
|
33
|
+
Project-URL: Repository, https://github.com/rhizomatics/updates2mqtt
|
|
34
|
+
Project-URL: Documentation, https://updates2mqtt.rhizomatics.org.uk
|
|
35
|
+
Project-URL: Issues, https://github.com/rhizomatics/updates2mqtt/issues
|
|
36
|
+
Project-URL: Changelog, https://github.com/rhizomatics/updates2mqtt/blob/main/CHANGELOG.md
|
|
36
37
|
Description-Content-Type: text/markdown
|
|
37
38
|
|
|
38
39
|
{ align=left }
|
|
@@ -42,7 +43,7 @@ Description-Content-Type: text/markdown
|
|
|
42
43
|
[](https://github.com/rhizomatics)
|
|
43
44
|
|
|
44
45
|
[](https://pypi.org/project/updates2mqtt/)
|
|
45
|
-
[](https://github.com/rhizomatics/
|
|
46
|
+
[](https://github.com/rhizomatics/updates2mqtt)
|
|
46
47
|
[](https://updates2mqtt.rhizomatics.org.uk/developer/coverage/)
|
|
47
48
|

|
|
48
49
|
[](https://results.pre-commit.ci/latest/github/rhizomatics/updates2mqtt/main)
|
|
@@ -69,7 +70,7 @@ Read the release notes, and optionally click *Update* to trigger a Docker *pull*
|
|
|
69
70
|
|
|
70
71
|
## Description
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
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
74
|
|
|
74
75
|
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
76
|
|
|
@@ -79,8 +80,14 @@ To get started, read the [Installation](installation.md) and [Configuration](con
|
|
|
79
80
|
|
|
80
81
|
For a quick spin, try this:
|
|
81
82
|
|
|
82
|
-
```
|
|
83
|
-
docker run -e MQTT_USER=user1 -e MQTT_PASS=
|
|
83
|
+
```bash
|
|
84
|
+
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
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
or without Docker, using [uv](https://docs.astral.sh/uv/)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
export MQTT_HOST=192.168.1.1;export MQTT_USER=user1;export MQTT_PASS=user1;uv run --with updates2mqtt python -m updates2mqtt
|
|
84
91
|
```
|
|
85
92
|
|
|
86
93
|
## Release Support
|
|
@@ -93,7 +100,7 @@ Presently only Docker containers are supported, although others are planned, pro
|
|
|
93
100
|
|
|
94
101
|
## Heartbeat
|
|
95
102
|
|
|
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
|
|
103
|
+
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
104
|
|
|
98
105
|
## Healthcheck
|
|
99
106
|
|
|
@@ -126,7 +133,8 @@ file, or as `environment` options inside `docker-compose.yaml`.
|
|
|
126
133
|
### Automated updates
|
|
127
134
|
|
|
128
135
|
If Docker containers should be immediately updated, without any confirmation
|
|
129
|
-
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`)
|
|
136
|
+
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`). If you want it to update without publishing to MQTT and being
|
|
137
|
+
visible to Home Assistant, then use `Silent`.
|
|
130
138
|
|
|
131
139
|
```yaml title="Example Compose Snippet"
|
|
132
140
|
restarter:
|
|
@@ -136,17 +144,42 @@ restarter:
|
|
|
136
144
|
- UPD2MQTT_UPDATE=AUTO
|
|
137
145
|
```
|
|
138
146
|
|
|
147
|
+
Automated updates can also apply to local builds, where a `git_repo_path` has been defined - if there are remote
|
|
148
|
+
commits available to pull, then a `git pull`, `docker compose build` and `docker compose up` will be executed.
|
|
149
|
+
|
|
139
150
|
### Environment Variables
|
|
140
151
|
|
|
141
152
|
The following environment variables can be used to configure containers for `updates2mqtt`:
|
|
142
153
|
|
|
143
|
-
| Env Var
|
|
144
|
-
|
|
145
|
-
| `UPD2MQTT_UPDATE`
|
|
146
|
-
| `UPD2MQTT_PICTURE`
|
|
147
|
-
| `UPD2MQTT_RELNOTES`
|
|
148
|
-
| `UPD2MQTT_GIT_REPO_PATH`
|
|
149
|
-
| `UPD2MQTT_IGNORE`
|
|
154
|
+
| Env Var | Description | Default |
|
|
155
|
+
|----------------------------|----------------------------------------------------------------------------------------------|-----------------|
|
|
156
|
+
| `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
|
|
157
|
+
| `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
|
|
158
|
+
| `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
|
|
159
|
+
| `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
|
|
160
|
+
| `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by Updates2MQTT. | False |
|
|
161
|
+
| |
|
|
162
|
+
|
|
163
|
+
### Docker Labels
|
|
164
|
+
|
|
165
|
+
Alternatively, use Docker labels
|
|
166
|
+
|
|
167
|
+
| Label | Env Var |
|
|
168
|
+
|--------------------------------|----------------------------|
|
|
169
|
+
| `updates2mqtt.update` | `UPD2MQTT_UPDATE` |
|
|
170
|
+
| `updates2mqtt.picture` | `UPD2MQTT_PCITURE` |
|
|
171
|
+
| `updates2mqtt.relnotes` | `UPD2MQTT_RELNOTES` |
|
|
172
|
+
| `updates2mqtt.git_repo_path` | `UPD2MQTT_GIT_REPO_PATH` |
|
|
173
|
+
| `updates2mqtt.ignore` | `UPD2MQTT_IGNORE` |
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
```yaml title="Example Compose Snippet"
|
|
177
|
+
restarter:
|
|
178
|
+
image: docker:cli
|
|
179
|
+
command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart mailserver; done"]
|
|
180
|
+
labels:
|
|
181
|
+
updates2mqtt.relnotes: https://component.my.com/release_notes
|
|
182
|
+
```
|
|
150
183
|
|
|
151
184
|
|
|
152
185
|
## Related Projects
|
|
@@ -157,6 +190,8 @@ Other apps useful for self-hosting with the help of MQTT:
|
|
|
157
190
|
|
|
158
191
|
Find more at [awesome-mqtt](https://github.com/rhizomatics/awesome-mqtt)
|
|
159
192
|
|
|
193
|
+
For a more powerful Docker update manager, try [What's Up Docker](https://getwud.github.io/wud/)
|
|
194
|
+
|
|
160
195
|
## Development
|
|
161
196
|
|
|
162
197
|
This component relies on several open source packages:
|
|
@@ -169,4 +204,4 @@ This component relies on several open source packages:
|
|
|
169
204
|
- [httpx](https://www.python-httpx.org) for retrieving metadata
|
|
170
205
|
- The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
|
|
171
206
|
- [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
|
|
207
|
+
- [usingversion](https://pypi.org/project/usingversion/) to log current version info
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/rhizomatics)
|
|
6
6
|
|
|
7
7
|
[](https://pypi.org/project/updates2mqtt/)
|
|
8
|
-
[](https://github.com/rhizomatics/
|
|
8
|
+
[](https://github.com/rhizomatics/updates2mqtt)
|
|
9
9
|
[](https://updates2mqtt.rhizomatics.org.uk/developer/coverage/)
|
|
10
10
|

|
|
11
11
|
[](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
|
-
|
|
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
|
-
```
|
|
46
|
-
docker run -e MQTT_USER=user1 -e MQTT_PASS=
|
|
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
|
|
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
|
|
|
@@ -89,7 +95,8 @@ file, or as `environment` options inside `docker-compose.yaml`.
|
|
|
89
95
|
### Automated updates
|
|
90
96
|
|
|
91
97
|
If Docker containers should be immediately updated, without any confirmation
|
|
92
|
-
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`)
|
|
98
|
+
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`). If you want it to update without publishing to MQTT and being
|
|
99
|
+
visible to Home Assistant, then use `Silent`.
|
|
93
100
|
|
|
94
101
|
```yaml title="Example Compose Snippet"
|
|
95
102
|
restarter:
|
|
@@ -99,17 +106,42 @@ restarter:
|
|
|
99
106
|
- UPD2MQTT_UPDATE=AUTO
|
|
100
107
|
```
|
|
101
108
|
|
|
109
|
+
Automated updates can also apply to local builds, where a `git_repo_path` has been defined - if there are remote
|
|
110
|
+
commits available to pull, then a `git pull`, `docker compose build` and `docker compose up` will be executed.
|
|
111
|
+
|
|
102
112
|
### Environment Variables
|
|
103
113
|
|
|
104
114
|
The following environment variables can be used to configure containers for `updates2mqtt`:
|
|
105
115
|
|
|
106
|
-
| Env Var
|
|
107
|
-
|
|
108
|
-
| `UPD2MQTT_UPDATE`
|
|
109
|
-
| `UPD2MQTT_PICTURE`
|
|
110
|
-
| `UPD2MQTT_RELNOTES`
|
|
111
|
-
| `UPD2MQTT_GIT_REPO_PATH`
|
|
112
|
-
| `UPD2MQTT_IGNORE`
|
|
116
|
+
| Env Var | Description | Default |
|
|
117
|
+
|----------------------------|----------------------------------------------------------------------------------------------|-----------------|
|
|
118
|
+
| `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
|
|
119
|
+
| `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
|
|
120
|
+
| `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
|
|
121
|
+
| `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
|
|
122
|
+
| `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by Updates2MQTT. | False |
|
|
123
|
+
| |
|
|
124
|
+
|
|
125
|
+
### Docker Labels
|
|
126
|
+
|
|
127
|
+
Alternatively, use Docker labels
|
|
128
|
+
|
|
129
|
+
| Label | Env Var |
|
|
130
|
+
|--------------------------------|----------------------------|
|
|
131
|
+
| `updates2mqtt.update` | `UPD2MQTT_UPDATE` |
|
|
132
|
+
| `updates2mqtt.picture` | `UPD2MQTT_PCITURE` |
|
|
133
|
+
| `updates2mqtt.relnotes` | `UPD2MQTT_RELNOTES` |
|
|
134
|
+
| `updates2mqtt.git_repo_path` | `UPD2MQTT_GIT_REPO_PATH` |
|
|
135
|
+
| `updates2mqtt.ignore` | `UPD2MQTT_IGNORE` |
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
```yaml title="Example Compose Snippet"
|
|
139
|
+
restarter:
|
|
140
|
+
image: docker:cli
|
|
141
|
+
command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart mailserver; done"]
|
|
142
|
+
labels:
|
|
143
|
+
updates2mqtt.relnotes: https://component.my.com/release_notes
|
|
144
|
+
```
|
|
113
145
|
|
|
114
146
|
|
|
115
147
|
## Related Projects
|
|
@@ -120,6 +152,8 @@ Other apps useful for self-hosting with the help of MQTT:
|
|
|
120
152
|
|
|
121
153
|
Find more at [awesome-mqtt](https://github.com/rhizomatics/awesome-mqtt)
|
|
122
154
|
|
|
155
|
+
For a more powerful Docker update manager, try [What's Up Docker](https://getwud.github.io/wud/)
|
|
156
|
+
|
|
123
157
|
## Development
|
|
124
158
|
|
|
125
159
|
This component relies on several open source packages:
|
|
@@ -132,4 +166,4 @@ This component relies on several open source packages:
|
|
|
132
166
|
- [httpx](https://www.python-httpx.org) for retrieving metadata
|
|
133
167
|
- The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
|
|
134
168
|
- [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
|
|
169
|
+
- [usingversion](https://pypi.org/project/usingversion/) to log current version info
|
|
@@ -7,9 +7,9 @@ authors = [
|
|
|
7
7
|
]
|
|
8
8
|
|
|
9
9
|
requires-python = ">=3.13"
|
|
10
|
-
version = "1.
|
|
10
|
+
version = "1.7.0"
|
|
11
11
|
license="Apache-2.0"
|
|
12
|
-
keywords=["mqtt", "docker", "updates", "automation","home-assistant","homeassistant","selfhosting"]
|
|
12
|
+
keywords=["mqtt", "docker", "oci","container","updates", "automation","home-assistant","homeassistant","selfhosting"]
|
|
13
13
|
|
|
14
14
|
dependencies = [
|
|
15
15
|
"docker>=7.1.0",
|
|
@@ -20,6 +20,7 @@ dependencies = [
|
|
|
20
20
|
"httpx>=0.28.1",
|
|
21
21
|
"hishel[httpx]>=0.1.4",
|
|
22
22
|
"usingversion>=0.1.2",
|
|
23
|
+
"tzlocal>=5.3.1",
|
|
23
24
|
]
|
|
24
25
|
classifiers = [
|
|
25
26
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -59,7 +60,7 @@ dev = [
|
|
|
59
60
|
"icdiff",
|
|
60
61
|
"genbadge[all]"
|
|
61
62
|
]
|
|
62
|
-
|
|
63
|
+
docs=[
|
|
63
64
|
"mkdocs",
|
|
64
65
|
"mkdocs-material",
|
|
65
66
|
"mkdocs-minify-plugin",
|
|
@@ -70,14 +71,17 @@ mkdocs=[
|
|
|
70
71
|
"pymdown-extensions",
|
|
71
72
|
"mkdocs-git-revision-date-localized-plugin",
|
|
72
73
|
"mkdocs-meta-descriptions-plugin",
|
|
73
|
-
"pngquant"
|
|
74
|
+
"pngquant",
|
|
75
|
+
"mkdocs-mermaid2-plugin"
|
|
74
76
|
]
|
|
75
77
|
|
|
76
78
|
[build-system]
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
]
|
|
79
|
+
requires = ["uv_build>=0.9.18,<0.10.0"]
|
|
80
|
+
build-backend = "uv_build"
|
|
81
|
+
|
|
82
|
+
[tool.uv.build-backend]
|
|
83
|
+
module-root = "src"
|
|
84
|
+
module-name = "updates2mqtt"
|
|
81
85
|
|
|
82
86
|
[tool.bandit]
|
|
83
87
|
exclude_dirs = ["tests"]
|
|
@@ -148,7 +152,6 @@ skip-magic-trailing-comma = false
|
|
|
148
152
|
# Like Black, automatically detect the appropriate line ending.
|
|
149
153
|
line-ending = "auto"
|
|
150
154
|
|
|
151
|
-
|
|
152
155
|
[tool.pytest.ini_options]
|
|
153
156
|
asyncio_mode = "auto"
|
|
154
157
|
testpaths = [
|
|
@@ -185,4 +188,4 @@ Homepage="https://updates2mqtt.rhizomatics.org.uk"
|
|
|
185
188
|
Repository="https://github.com/rhizomatics/updates2mqtt"
|
|
186
189
|
Documentation="https://updates2mqtt.rhizomatics.org.uk"
|
|
187
190
|
Issues="https://github.com/rhizomatics/updates2mqtt/issues"
|
|
188
|
-
Changelog="https://github.com/rhizomatics/updates2mqtt/blob/main/CHANGELOG.md"
|
|
191
|
+
Changelog="https://github.com/rhizomatics/updates2mqtt/blob/main/CHANGELOG.md"
|
|
@@ -14,7 +14,7 @@ import structlog
|
|
|
14
14
|
import updates2mqtt
|
|
15
15
|
from updates2mqtt.model import Discovery, ReleaseProvider
|
|
16
16
|
|
|
17
|
-
from .config import Config,
|
|
17
|
+
from .config import Config, PublishPolicy, UpdatePolicy, load_app_config
|
|
18
18
|
from .integrations.docker import DockerProvider
|
|
19
19
|
from .mqtt import MqttPublisher
|
|
20
20
|
|
|
@@ -39,14 +39,16 @@ 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}
|
|
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)
|
|
49
|
-
self.common_pkg: dict[str, PackageUpdateInfo] = load_package_info(PKG_INFO_FILE)
|
|
50
52
|
|
|
51
53
|
self.publisher = MqttPublisher(self.cfg.mqtt, self.cfg.node, self.cfg.homeassistant)
|
|
52
54
|
|
|
@@ -54,7 +56,7 @@ class App:
|
|
|
54
56
|
self.scan_count: int = 0
|
|
55
57
|
self.last_scan: str | None = None
|
|
56
58
|
if self.cfg.docker.enabled:
|
|
57
|
-
self.scanners.append(DockerProvider(self.cfg.docker, self.
|
|
59
|
+
self.scanners.append(DockerProvider(self.cfg.docker, self.cfg.node, self.self_bounce))
|
|
58
60
|
self.stopped = Event()
|
|
59
61
|
self.healthcheck_topic = self.cfg.node.healthcheck.topic_template.format(node_name=self.cfg.node.name)
|
|
60
62
|
|
|
@@ -101,6 +103,7 @@ class App:
|
|
|
101
103
|
)
|
|
102
104
|
|
|
103
105
|
for scanner in self.scanners:
|
|
106
|
+
scanner.initialize()
|
|
104
107
|
self.publisher.subscribe_hass_command(scanner)
|
|
105
108
|
|
|
106
109
|
while not self.stopped.is_set() and self.publisher.is_available():
|
|
@@ -119,11 +122,18 @@ class App:
|
|
|
119
122
|
async def on_discovery(self, discovery: Discovery) -> None:
|
|
120
123
|
dlog = log.bind(name=discovery.name)
|
|
121
124
|
try:
|
|
122
|
-
if self.cfg.homeassistant.discovery.enabled:
|
|
125
|
+
if discovery.publish_policy == PublishPolicy.HOMEASSISTANT and self.cfg.homeassistant.discovery.enabled:
|
|
126
|
+
# Switch off MQTT discovery if not Home Assistant enabled
|
|
123
127
|
self.publisher.publish_hass_config(discovery)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if discovery.
|
|
128
|
+
if discovery.publish_policy in (PublishPolicy.HOMEASSISTANT):
|
|
129
|
+
self.publisher.publish_hass_state(discovery)
|
|
130
|
+
if discovery.publish_policy in (PublishPolicy.HOMEASSISTANT, PublishPolicy.MQTT):
|
|
131
|
+
self.publisher.publish_discovery(discovery)
|
|
132
|
+
if (
|
|
133
|
+
discovery.update_policy == UpdatePolicy.AUTO
|
|
134
|
+
and discovery.can_update
|
|
135
|
+
and discovery.latest_version != discovery.current_version
|
|
136
|
+
):
|
|
127
137
|
# TODO: review auto update, trigger by version, use update interval as throttle
|
|
128
138
|
elapsed: float = (
|
|
129
139
|
time.time() - discovery.update_last_attempt if discovery.update_last_attempt is not None else -1
|
|
@@ -155,7 +165,11 @@ class App:
|
|
|
155
165
|
log.debug("Cancellation task completed")
|
|
156
166
|
|
|
157
167
|
def shutdown(self, *args, exit_code: int = 143) -> None: # noqa: ANN002, ARG002
|
|
158
|
-
|
|
168
|
+
if self.self_bounce.is_set():
|
|
169
|
+
exit_code = 1
|
|
170
|
+
log.info("Self bouncing, overriding exit_code: %s", exit_code)
|
|
171
|
+
else:
|
|
172
|
+
log.info("Shutting down, exit_code: %s", exit_code)
|
|
159
173
|
self.stopped.set()
|
|
160
174
|
for scanner in self.scanners:
|
|
161
175
|
scanner.stop()
|
|
@@ -206,8 +220,6 @@ def run() -> None:
|
|
|
206
220
|
import asyncio
|
|
207
221
|
import signal
|
|
208
222
|
|
|
209
|
-
from .app import App
|
|
210
|
-
|
|
211
223
|
# pyright: ignore[reportAttributeAccessIssue]
|
|
212
224
|
log.debug(f"Starting updates2mqtt v{updates2mqtt.version}") # pyright: ignore[reportAttributeAccessIssue]
|
|
213
225
|
app = App()
|
|
@@ -9,6 +9,20 @@ from omegaconf import MISSING, DictConfig, MissingMandatoryValue, OmegaConf, Val
|
|
|
9
9
|
|
|
10
10
|
log = structlog.get_logger()
|
|
11
11
|
|
|
12
|
+
PKG_INFO_FILE = Path("./common_packages.yaml")
|
|
13
|
+
NO_KNOWN_IMAGE = "UNKNOWN"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UpdatePolicy(StrEnum):
|
|
17
|
+
AUTO = "Auto"
|
|
18
|
+
PASSIVE = "Passive"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PublishPolicy(StrEnum):
|
|
22
|
+
HOMEASSISTANT = "HomeAssistant"
|
|
23
|
+
MQTT = "MQTT"
|
|
24
|
+
SILENT = "Silent"
|
|
25
|
+
|
|
12
26
|
|
|
13
27
|
class LogLevel(StrEnum):
|
|
14
28
|
DEBUG = "DEBUG"
|
|
@@ -18,6 +32,13 @@ class LogLevel(StrEnum):
|
|
|
18
32
|
CRITICAL = "CRITICAL"
|
|
19
33
|
|
|
20
34
|
|
|
35
|
+
class VersionType:
|
|
36
|
+
SHORT_SHA = "short_sha"
|
|
37
|
+
FULL_SHA = "full_sha"
|
|
38
|
+
VERSION_REVISION = "version_revision"
|
|
39
|
+
VERSION = "version"
|
|
40
|
+
|
|
41
|
+
|
|
21
42
|
@dataclass
|
|
22
43
|
class MqttConfig:
|
|
23
44
|
host: str = "${oc.env:MQTT_HOST,localhost}"
|
|
@@ -34,6 +55,12 @@ class MetadataSourceConfig:
|
|
|
34
55
|
cache_ttl: int = 60 * 60 * 24 * 7 # 1 week
|
|
35
56
|
|
|
36
57
|
|
|
58
|
+
@dataclass
|
|
59
|
+
class Selector:
|
|
60
|
+
include: list[str] | None = None
|
|
61
|
+
exclude: list[str] | None = None
|
|
62
|
+
|
|
63
|
+
|
|
37
64
|
@dataclass
|
|
38
65
|
class DockerConfig:
|
|
39
66
|
enabled: bool = True
|
|
@@ -47,6 +74,8 @@ class DockerConfig:
|
|
|
47
74
|
discover_metadata: dict[str, MetadataSourceConfig] = field(
|
|
48
75
|
default_factory=lambda: {"linuxserver.io": MetadataSourceConfig(enabled=True)}
|
|
49
76
|
)
|
|
77
|
+
default_api_backoff: int = 60 * 15
|
|
78
|
+
image_ref_select: Selector = field(default_factory=lambda: Selector())
|
|
50
79
|
|
|
51
80
|
|
|
52
81
|
@dataclass
|
|
@@ -61,6 +90,7 @@ class HomeAssistantConfig:
|
|
|
61
90
|
state_topic_suffix: str = "state"
|
|
62
91
|
device_creation: bool = True
|
|
63
92
|
force_command_topic: bool = False
|
|
93
|
+
extra_attributes: bool = True
|
|
64
94
|
area: str | None = None
|
|
65
95
|
|
|
66
96
|
|
|
@@ -114,24 +144,6 @@ class IncompleteConfigException(BaseException):
|
|
|
114
144
|
pass
|
|
115
145
|
|
|
116
146
|
|
|
117
|
-
def load_package_info(pkginfo_file_path: Path) -> dict[str, PackageUpdateInfo]:
|
|
118
|
-
if pkginfo_file_path.exists():
|
|
119
|
-
log.debug("Loading common package update info", path=pkginfo_file_path)
|
|
120
|
-
cfg = OmegaConf.load(pkginfo_file_path)
|
|
121
|
-
else:
|
|
122
|
-
log.warn("No common package update info found", path=pkginfo_file_path)
|
|
123
|
-
cfg = OmegaConf.structured(UpdateInfoConfig)
|
|
124
|
-
try:
|
|
125
|
-
# omegaconf broken-ness on optional fields and converting to backclasses
|
|
126
|
-
pkg_conf: dict[str, PackageUpdateInfo] = {
|
|
127
|
-
pkg: PackageUpdateInfo(**pkg_cfg) for pkg, pkg_cfg in cfg.common_packages.items()
|
|
128
|
-
}
|
|
129
|
-
return pkg_conf
|
|
130
|
-
except (MissingMandatoryValue, ValidationError) as e:
|
|
131
|
-
log.error("Configuration error %s", e, path=pkginfo_file_path.as_posix())
|
|
132
|
-
raise
|
|
133
|
-
|
|
134
|
-
|
|
135
147
|
def is_autogen_config() -> bool:
|
|
136
148
|
env_var: str | None = os.environ.get("U2M_AUTOGEN_CONFIG")
|
|
137
149
|
return not (env_var and env_var.lower() in ("no", "0", "false"))
|
|
@@ -23,28 +23,26 @@ def hass_format_config(
|
|
|
23
23
|
object_id: str,
|
|
24
24
|
state_topic: str,
|
|
25
25
|
command_topic: str | None,
|
|
26
|
+
attrs_topic: str | None,
|
|
26
27
|
force_command_topic: bool | None,
|
|
27
28
|
device_creation: bool = True,
|
|
28
29
|
area: str | None = None,
|
|
29
|
-
session: str | None = None,
|
|
30
30
|
) -> dict[str, Any]:
|
|
31
31
|
config: dict[str, Any] = {
|
|
32
32
|
"name": discovery.title,
|
|
33
33
|
"device_class": None, # not firmware, so defaults to null
|
|
34
34
|
"unique_id": object_id,
|
|
35
35
|
"state_topic": state_topic,
|
|
36
|
-
"source_session": session,
|
|
37
36
|
"supported_features": discovery.features,
|
|
38
|
-
"
|
|
39
|
-
"can_build": discovery.can_build,
|
|
40
|
-
"can_restart": discovery.can_restart,
|
|
41
|
-
"update_policy": discovery.update_policy,
|
|
37
|
+
"default_entity_id": f"update.{discovery.node}_{discovery.provider.source_type}_{discovery.name}",
|
|
42
38
|
"origin": {
|
|
43
39
|
"name": f"{discovery.node} updates2mqtt",
|
|
44
40
|
"sw_version": updates2mqtt.version, # pyright: ignore[reportAttributeAccessIssue]
|
|
45
41
|
"support_url": "https://github.com/rhizomatics/updates2mqtt/issues",
|
|
46
42
|
},
|
|
47
43
|
}
|
|
44
|
+
if attrs_topic:
|
|
45
|
+
config["json_attributes_topic"] = attrs_topic
|
|
48
46
|
if discovery.entity_picture_url:
|
|
49
47
|
config["entity_picture"] = discovery.entity_picture_url
|
|
50
48
|
if discovery.device_icon:
|
|
@@ -62,14 +60,12 @@ def hass_format_config(
|
|
|
62
60
|
config["command_topic"] = command_topic
|
|
63
61
|
if discovery.can_update:
|
|
64
62
|
config["payload_install"] = f"{discovery.source_type}|{discovery.name}|install"
|
|
65
|
-
|
|
66
|
-
config["git_repo_path"] = discovery.custom["git_repo_path"]
|
|
67
|
-
config.update(discovery.provider.hass_config_format(discovery))
|
|
63
|
+
|
|
68
64
|
return config
|
|
69
65
|
|
|
70
66
|
|
|
71
67
|
def hass_format_state(discovery: Discovery, session: str, in_progress: bool = False) -> dict[str, Any]: # noqa: ARG001
|
|
72
|
-
state = {
|
|
68
|
+
state: dict[str, str | dict | list | bool | None] = {
|
|
73
69
|
"installed_version": discovery.current_version,
|
|
74
70
|
"latest_version": discovery.latest_version,
|
|
75
71
|
"title": discovery.title,
|
|
@@ -79,11 +75,5 @@ def hass_format_state(discovery: Discovery, session: str, in_progress: bool = Fa
|
|
|
79
75
|
state["release_summary"] = discovery.release_summary
|
|
80
76
|
if discovery.release_url:
|
|
81
77
|
state["release_url"] = discovery.release_url
|
|
82
|
-
|
|
83
|
-
if custom_state:
|
|
84
|
-
state.update(custom_state)
|
|
85
|
-
invalid_keys = [k for k in state if k not in HASS_UPDATE_SCHEMA]
|
|
86
|
-
if invalid_keys:
|
|
87
|
-
log.warning(f"Invalid keys in state: {invalid_keys}")
|
|
88
|
-
state = {k: v for k, v in state.items() if k in HASS_UPDATE_SCHEMA}
|
|
78
|
+
|
|
89
79
|
return state
|