updates2mqtt 1.3.7__py3-none-any.whl → 1.4.0__py3-none-any.whl

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/config.py CHANGED
@@ -16,6 +16,7 @@ class MqttConfig:
16
16
  password: str = f"${{oc.env:MQTT_PASS,{MISSING}}}"
17
17
  port: int = "${oc.decode:${oc.env:MQTT_PORT,1883}}" # type: ignore[assignment]
18
18
  topic_root: str = "updates2mqtt"
19
+ protocol: str = "3.11"
19
20
 
20
21
 
21
22
  @dataclass
@@ -287,7 +287,7 @@ class DockerProvider(ReleaseProvider):
287
287
  logger.info(f"Shutdown detected, aborting scan at {c}")
288
288
  break
289
289
  containers = containers + 1
290
- result = self.analyze(cast("Container", c), session)
290
+ result = self.analyze(c, session)
291
291
  if result:
292
292
  self.discoveries[result.name] = result
293
293
  results = results + 1
@@ -28,8 +28,10 @@ def git_timestamp(repo_path: Path, git_path: Path) -> datetime.datetime | None:
28
28
  check=True,
29
29
  )
30
30
  return datetime.datetime.fromisoformat(result.stdout.strip())
31
+ except subprocess.CalledProcessError as cpe:
32
+ log.warn("GIT No result from git log at %s: %s", repo_path, cpe)
31
33
  except Exception as e:
32
- log.warn("GIT Unable to parse timestamp at %s - %s: %s", repo_path, result.stdout if result else "<NO RESULT>", e)
34
+ log.error("GIT Unable to parse timestamp at %s - %s: %s", repo_path, result.stdout if result else "<NO RESULT>", e)
33
35
  return None
34
36
 
35
37
 
updates2mqtt/mqtt.py CHANGED
@@ -9,8 +9,8 @@ from typing import Any
9
9
  import paho.mqtt.client as mqtt
10
10
  import paho.mqtt.subscribeoptions
11
11
  import structlog
12
- from paho.mqtt.client import MQTTMessage
13
- from paho.mqtt.enums import CallbackAPIVersion, MQTTErrorCode
12
+ from paho.mqtt.client import MQTT_CLEAN_START_FIRST_ONLY, MQTTMessage
13
+ from paho.mqtt.enums import CallbackAPIVersion, MQTTErrorCode, MQTTProtocolVersion
14
14
  from paho.mqtt.properties import Properties
15
15
  from paho.mqtt.reasoncodes import ReasonCode
16
16
 
@@ -42,19 +42,39 @@ class MqttClient:
42
42
  def start(self, event_loop: asyncio.AbstractEventLoop | None = None) -> None:
43
43
  logger = self.log.bind(action="start")
44
44
  try:
45
+ protocol: MQTTProtocolVersion
46
+ if self.cfg.protocol in ("3", "3.11"):
47
+ protocol = MQTTProtocolVersion.MQTTv311
48
+ elif self.cfg.protocol == "3.1":
49
+ protocol = MQTTProtocolVersion.MQTTv31
50
+ elif self.cfg.protocol in ("5", "5.0"):
51
+ protocol = MQTTProtocolVersion.MQTTv5
52
+ else:
53
+ self.log.info("No valid MQTT protocol version found (%s), setting to default v3.11", self.cfg.protocol)
54
+ protocol = MQTTProtocolVersion.MQTTv311
55
+ self.log.debug("MQTT protocol set to %r", protocol)
56
+
45
57
  self.event_loop = event_loop or asyncio.get_event_loop()
46
58
  self.client = mqtt.Client(
47
59
  callback_api_version=CallbackAPIVersion.VERSION2,
48
60
  client_id=f"updates2mqtt_{self.node_cfg.name}",
49
- clean_session=True,
61
+ clean_session=True if protocol != MQTTProtocolVersion.MQTTv5 else None,
62
+ protocol=protocol,
50
63
  )
51
64
  self.client.username_pw_set(self.cfg.user, password=self.cfg.password)
52
- rc: MQTTErrorCode = self.client.connect(host=self.cfg.host, port=self.cfg.port, keepalive=60)
65
+ rc: MQTTErrorCode = self.client.connect(
66
+ host=self.cfg.host,
67
+ port=self.cfg.port,
68
+ keepalive=60,
69
+ clean_start=MQTT_CLEAN_START_FIRST_ONLY,
70
+ )
53
71
  self.log.info("Client connection requested", result_code=rc)
54
72
 
55
73
  self.client.on_connect = self.on_connect
56
74
  self.client.on_disconnect = self.on_disconnect
57
75
  self.client.on_message = self.on_message
76
+ self.client.on_subscribe = self.on_subscribe
77
+ self.client.on_unsubscribe = self.on_unsubscribe
58
78
 
59
79
  self.client.loop_start()
60
80
 
@@ -223,6 +243,30 @@ class MqttClient:
223
243
  )
224
244
  self.handle_message(msg)
225
245
 
246
+ def on_subscribe(
247
+ self,
248
+ _client: mqtt.Client,
249
+ userdata: Any,
250
+ mid: int,
251
+ reason_code_list: list[ReasonCode],
252
+ properties: Properties | None = None,
253
+ ) -> None:
254
+ self.log.debug(
255
+ "on_subscribe, userdata=%s, mid=%s, reasons=%s, properties=%s", userdata, mid, reason_code_list, properties
256
+ )
257
+
258
+ def on_unsubscribe(
259
+ self,
260
+ _client: mqtt.Client,
261
+ userdata: Any,
262
+ mid: int,
263
+ reason_code_list: list[ReasonCode],
264
+ properties: Properties | None = None,
265
+ ) -> None:
266
+ self.log.debug(
267
+ "on_unsubscribe, userdata=%s, mid=%s, reasons=%s, properties=%s", userdata, mid, reason_code_list, properties
268
+ )
269
+
226
270
  def on_message(self, _client: mqtt.Client, _userdata: Any, msg: mqtt.MQTTMessage) -> None:
227
271
  """Callback for incoming MQTT messages""" # noqa: D401
228
272
  if msg.topic in self.providers_by_topic:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: updates2mqtt
3
- Version: 1.3.7
3
+ Version: 1.4.0
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
@@ -49,9 +49,14 @@ Description-Content-Type: text/markdown
49
49
 
50
50
  ## Summary
51
51
 
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*.
52
+ Let Home Assistant tell you about new updates to Docker images for your containers.
53
+
54
+ ![Example Home Assistant update page](images/ha_update_detail.png "Home Assistant Updates")
55
+
56
+ Read the release notes, and optionally click *Update* to trigger a Docker *pull* (or optionally *build*) and *update*.
57
+
58
+ ![Example Home Assistant update dialog](images/ha_update_dialog.png "Home Assistant Updates"){width=480}
53
59
 
54
- ![Example Home Assistant update dialog](images/ha_update_detail.png "Home Assistant Updates")
55
60
 
56
61
  ## Description
57
62
 
@@ -59,7 +64,7 @@ updates2mqtt perioidically checks for new versions of components being available
59
64
 
60
65
  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
66
 
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.
67
+ 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
68
 
64
69
  To get started, read the [Installation](installation.md) and [Configuration](configuration.md) pages.
65
70
 
@@ -71,8 +76,7 @@ docker run -e MQTT_USER=user1 -e MQTT_PASS=pass1 -e MQTT_HOST=192.168.1.5 ghcr.i
71
76
 
72
77
  ## Release Support
73
78
 
74
- Presently only Docker containers are supported, although others are planned,
75
- probably with priority for `apt`.
79
+ Presently only Docker containers are supported, although others are planned, probably with priority for `apt`.
76
80
 
77
81
  | Ecosystem | Support | Comments |
78
82
  |-----------|-------------|----------------------------------------------------------------------------------------------------|
@@ -82,31 +86,57 @@ probably with priority for `apt`.
82
86
 
83
87
  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
88
 
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`.
89
+ 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
90
 
87
- TIP: Check healthcheck is working using `docker inspect --format "{{json .State.Health }}" updates2mqtt | jq`
88
-
89
- ## HomeAssistant integration
91
+ !!! tip
92
+
93
+ Check healthcheck is working using `docker inspect --format "{{json .State.Health }}" updates2mqtt | jq`
94
+
95
+ Another approach is using a restarter service directly in Docker Compose to force a restart, in this case once a day:
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
+ ```yaml title="Example Compose Service"
98
+ restarter:
99
+ image: docker:cli
100
+ volumes: ["/var/run/docker.sock:/var/run/docker.sock"]
101
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart updates2mqtt; done"]
102
+ restart: unless-stopped
103
+ environment:
104
+ - UPD2MQTT_UPDATE=AUTO
105
+ ```
106
+
107
+ ## Target Containers
108
+
109
+ While `updates2mqtt` will discover and monitor all containers running under the Docker daemon,
110
+ there are some options to make to those containers to tune how it works.
93
111
 
94
- ![Home Assistant MQTT Integraion configuration](images/ha_mqtt_discovery.png "Home Assistant MQTT Discovery")
112
+ These happen by adding environment variables to the containers, typically inside an `.env`
113
+ file, or as `environment` options inside `docker-compose.yaml`.
95
114
 
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.
115
+ ### Automated updates
97
116
 
98
- ![Home Assistant updates in Settings](images/ha_update_page.png "Home Assistant Updates")
117
+ If Docker containers should be immediately updated, without any confirmation
118
+ 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`)
99
119
 
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.
120
+ ```yaml title="Example Compose Snippet"
121
+ restarter:
122
+ image: docker:cli
123
+ command: ["/bin/sh", "-c", "while true; do sleep 86400; docker restart mailserver; done"]
124
+ environment:
125
+ - UPD2MQTT_UPDATE=AUTO
126
+ ```
127
+
128
+ ### Environment Variables
101
129
 
102
- There are 3 separate types of MQTT topic used for HomeAssisstant integration:
130
+ The following environment variables can be used to configure containers for `updates2mqtt`:
103
131
 
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`
132
+ | Env Var | Description | Default |
133
+ |---------| ------------|----------|
134
+ | `UPD2MQTT_UPDATE` | Update mode, either `Passive` or `Auto`. If `Auto`, updates will be installed automatically. | `Passive` |
135
+ | `UPD2MQTT_PICTURE` | URL to an icon to use in Home Assistant. | Docker logo URL |
136
+ | `UPD2MQTT_RELNOTES` | URL to release notes for the package. | |
137
+ | `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
138
+ | `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by updates2mqtt. | False |
107
139
 
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
140
 
111
141
  ## Related Projects
112
142
 
@@ -114,6 +144,18 @@ Other apps useful for self-hosting with the help of MQTT:
114
144
 
115
145
  - [psmqtt](https://github.com/eschava/psmqtt) - Report system health and metrics via MQTT
116
146
 
147
+ Find more at [awesome-mqtt](https://github.com/rhizomatics/awesome-mqtt)
148
+
117
149
  ## Development
118
150
 
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.
151
+ This component relies on several open source packages:
152
+
153
+ - [docker-py](https://docker-py.readthedocs.io/en/stable/) SDK for Python for access to Docker APIs
154
+ - [Eclipse Paho](https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html) MQTT client
155
+ - [OmegaConf](https://omegaconf.readthedocs.io) for configuration and validation
156
+ - [structlog](https://www.structlog.org/en/stable/) for structured logging and [rich](https://rich.readthedocs.io/en/stable/) for better exception reporting
157
+ - [hishel](https://hishel.com/1.0/) for caching metadata
158
+ - [httpx](https://www.python-httpx.org) for retrieving metadata
159
+ - The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
160
+ - [pytest](https://docs.pytest.org/en/stable/) and supporting add-ins for automated testing
161
+ - [usingversion](https://pypi.org/project/usingversion/) to log current version info
@@ -0,0 +1,16 @@
1
+ updates2mqtt/__init__.py,sha256=gnmHrLOSYc-N1-c5VG46OpNpoXEybKzYhEvFMm955P8,237
2
+ updates2mqtt/__main__.py,sha256=HBF00oH5fhS33sI_CdbxNlaUvbIzuuGxwnRYdhHqx0M,194
3
+ updates2mqtt/app.py,sha256=7jnmtIkXlX4e4lIt8WCzV19IYrICkA7cU4m9u9QXvRU,8229
4
+ updates2mqtt/config.py,sha256=SK6uhDyUb9C2JYVd0j6KBHzSAfaCFcOUbmmgsq6VSs0,5027
5
+ updates2mqtt/hass_formatter.py,sha256=Ulfj8F0e_1QMmRuJzHsNM2WxHbz9sIkWOWjRq3kQZzs,2772
6
+ updates2mqtt/model.py,sha256=5tWlj3appGGZjkuBeYR2lb-kXoy5mzCn4P_EJQjnwok,3676
7
+ updates2mqtt/mqtt.py,sha256=LDy9x7Mmq_Em6lV-w9J3TjzBCuu8eehI8PK4E0i015A,14468
8
+ updates2mqtt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ updates2mqtt/integrations/__init__.py,sha256=KmNTUxvVWvqI7rl4I0xZg7XaCmcMS2O4OSv-ClsWM4Q,109
10
+ updates2mqtt/integrations/docker.py,sha256=c1y3Xkv57_frxZOlq9LoYQRnfX-1XeedCjDcBLx1it0,18849
11
+ updates2mqtt/integrations/git_utils.py,sha256=bPCmQiZpKpMcrGI7xAVmePXHFn8WwjcPNkf7xqDsGQA,2319
12
+ updates2mqtt-1.4.0.dist-info/METADATA,sha256=0wYbNmGsu4nQF5C49M0pegMkpmMgW7rNR5n6FTbTwlA,8916
13
+ updates2mqtt-1.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ updates2mqtt-1.4.0.dist-info/entry_points.txt,sha256=Hc6NZ2dBevYSUKTJU6NOs8Mw7Vt0S-9lq5FuKb76NCc,54
15
+ updates2mqtt-1.4.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
16
+ updates2mqtt-1.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,16 +0,0 @@
1
- updates2mqtt/__init__.py,sha256=gnmHrLOSYc-N1-c5VG46OpNpoXEybKzYhEvFMm955P8,237
2
- updates2mqtt/__main__.py,sha256=HBF00oH5fhS33sI_CdbxNlaUvbIzuuGxwnRYdhHqx0M,194
3
- updates2mqtt/app.py,sha256=7jnmtIkXlX4e4lIt8WCzV19IYrICkA7cU4m9u9QXvRU,8229
4
- updates2mqtt/config.py,sha256=NiaFdMTXXAjZIbtW7LHYjSqu-ONEoouT2uUu506CTtM,5000
5
- updates2mqtt/hass_formatter.py,sha256=Ulfj8F0e_1QMmRuJzHsNM2WxHbz9sIkWOWjRq3kQZzs,2772
6
- updates2mqtt/model.py,sha256=5tWlj3appGGZjkuBeYR2lb-kXoy5mzCn4P_EJQjnwok,3676
7
- updates2mqtt/mqtt.py,sha256=i2l1BlEmnkp3Ie2qeAPVmdhIO1I_mH8Zxbm84cIYtGI,12741
8
- updates2mqtt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- updates2mqtt/integrations/__init__.py,sha256=KmNTUxvVWvqI7rl4I0xZg7XaCmcMS2O4OSv-ClsWM4Q,109
10
- updates2mqtt/integrations/docker.py,sha256=OX_sXtWVgUJfRSFi_tTBib4gvP_foQI4MoeTkTeLbZc,18868
11
- updates2mqtt/integrations/git_utils.py,sha256=SkAp6XcvCHwaiy17t6F97kcWTjBd7RyEmfhz6M_EhP0,2196
12
- updates2mqtt-1.3.7.dist-info/METADATA,sha256=yFJy-VGcnmHYdFksLx2teBHbuPi-B3J7pObaE5-XVEk,7990
13
- updates2mqtt-1.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- updates2mqtt-1.3.7.dist-info/entry_points.txt,sha256=Hc6NZ2dBevYSUKTJU6NOs8Mw7Vt0S-9lq5FuKb76NCc,54
15
- updates2mqtt-1.3.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
16
- updates2mqtt-1.3.7.dist-info/RECORD,,