updates2mqtt 1.7.0__py3-none-any.whl → 1.7.2__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/cli.py +150 -0
- updates2mqtt/config.py +32 -2
- updates2mqtt/hass_formatter.py +5 -4
- updates2mqtt/helpers.py +226 -0
- updates2mqtt/integrations/docker.py +308 -252
- updates2mqtt/integrations/docker_enrich.py +714 -182
- updates2mqtt/integrations/git_utils.py +5 -5
- updates2mqtt/model.py +94 -89
- updates2mqtt/mqtt.py +5 -0
- {updates2mqtt-1.7.0.dist-info → updates2mqtt-1.7.2.dist-info}/METADATA +13 -7
- updates2mqtt-1.7.2.dist-info/RECORD +18 -0
- {updates2mqtt-1.7.0.dist-info → updates2mqtt-1.7.2.dist-info}/entry_points.txt +1 -0
- updates2mqtt-1.7.0.dist-info/RECORD +0 -16
- {updates2mqtt-1.7.0.dist-info → updates2mqtt-1.7.2.dist-info}/WHEEL +0 -0
|
@@ -44,7 +44,7 @@ def git_iso_timestamp(repo_path: Path, git_path: Path) -> str | None:
|
|
|
44
44
|
return None
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def
|
|
47
|
+
def git_local_digest(repo_path: Path, git_path: Path) -> str | None:
|
|
48
48
|
result = None
|
|
49
49
|
try:
|
|
50
50
|
result = subprocess.run(
|
|
@@ -56,17 +56,17 @@ def git_local_version(repo_path: Path, git_path: Path) -> str | None:
|
|
|
56
56
|
check=True,
|
|
57
57
|
)
|
|
58
58
|
if result.returncode == 0:
|
|
59
|
-
log.debug("Local git rev-parse", action="
|
|
60
|
-
return
|
|
59
|
+
log.debug("Local git rev-parse", action="git_local_digest", path=repo_path, version=result.stdout.strip())
|
|
60
|
+
return result.stdout.strip()[:15]
|
|
61
61
|
except subprocess.CalledProcessError as cpe:
|
|
62
|
-
log.warn("GIT No result from git rev-parse at %s: %s", repo_path, cpe, action="
|
|
62
|
+
log.warn("GIT No result from git rev-parse at %s: %s", repo_path, cpe, action="git_local_digest")
|
|
63
63
|
except Exception as e:
|
|
64
64
|
log.error(
|
|
65
65
|
"GIT Unable to retrieve version at %s - %s: %s",
|
|
66
66
|
repo_path,
|
|
67
67
|
result.stdout if result else "<NO RESULT>",
|
|
68
68
|
e,
|
|
69
|
-
action="
|
|
69
|
+
action="git_local_digest",
|
|
70
70
|
)
|
|
71
71
|
return None
|
|
72
72
|
|
updates2mqtt/model.py
CHANGED
|
@@ -1,33 +1,63 @@
|
|
|
1
|
-
import datetime as dt
|
|
2
1
|
import json
|
|
3
|
-
import re
|
|
4
2
|
import time
|
|
5
3
|
from abc import abstractmethod
|
|
6
4
|
from collections.abc import AsyncGenerator, Callable
|
|
7
|
-
from enum import StrEnum
|
|
8
5
|
from threading import Event
|
|
9
6
|
from typing import Any
|
|
10
7
|
|
|
11
8
|
import structlog
|
|
12
|
-
from tzlocal import get_localzone
|
|
13
9
|
|
|
14
|
-
from updates2mqtt.config import
|
|
10
|
+
from updates2mqtt.config import NodeConfig, PublishPolicy, UpdatePolicy, VersionPolicy
|
|
11
|
+
from updates2mqtt.helpers import timestamp
|
|
15
12
|
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return None
|
|
20
|
-
try:
|
|
21
|
-
return dt.datetime.fromtimestamp(time_value, tz=get_localzone()).isoformat()
|
|
22
|
-
except: # noqa: E722
|
|
23
|
-
return None
|
|
14
|
+
class DiscoveryArtefactDetail:
|
|
15
|
+
"""Provider specific detail"""
|
|
24
16
|
|
|
17
|
+
def as_dict(self) -> dict[str, str | list | dict | bool | int | None]:
|
|
18
|
+
return {}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DiscoveryInstallationDetail:
|
|
22
|
+
"""Provider specific detail"""
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def as_dict(self) -> dict[str, str | list | dict | bool | int | None]:
|
|
26
|
+
return {}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
|
|
29
|
+
class ReleaseDetail:
|
|
30
|
+
"""The artefact source details
|
|
31
|
+
|
|
32
|
+
Note this may be an actual software package, or the source details of the wrapping of it
|
|
33
|
+
For example, some Docker images report the main source repo, and others where the Dockerfile deploy project lives
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, notes_url: str | None = None, summary: str | None = None) -> None:
|
|
37
|
+
self.source_platform: str | None = None
|
|
38
|
+
self.source_repo_url: str | None = None
|
|
39
|
+
self.source_url: str | None = None
|
|
40
|
+
self.version: str | None = None
|
|
41
|
+
self.revision: str | None = None
|
|
42
|
+
self.diff_url: str | None = None
|
|
43
|
+
self.notes_url: str | None = notes_url
|
|
44
|
+
self.title: str | None = None
|
|
45
|
+
self.summary: str | None = summary
|
|
46
|
+
self.net_score: int | None = None
|
|
47
|
+
|
|
48
|
+
def as_dict(self) -> dict[str, str | None]:
|
|
49
|
+
return {
|
|
50
|
+
"title": self.title,
|
|
51
|
+
"version": self.version,
|
|
52
|
+
"source_platform": self.source_platform,
|
|
53
|
+
"source_repo": self.source_repo_url,
|
|
54
|
+
"source": self.source_url,
|
|
55
|
+
"revision": self.revision,
|
|
56
|
+
"diff_url": self.diff_url,
|
|
57
|
+
"notes_url": self.notes_url,
|
|
58
|
+
"summary": self.summary,
|
|
59
|
+
"net_score": str(self.net_score) if self.net_score is not None else None,
|
|
60
|
+
}
|
|
31
61
|
|
|
32
62
|
|
|
33
63
|
class Discovery:
|
|
@@ -42,22 +72,24 @@ class Discovery:
|
|
|
42
72
|
entity_picture_url: str | None = None,
|
|
43
73
|
current_version: str | None = None,
|
|
44
74
|
latest_version: str | None = None,
|
|
45
|
-
can_update: bool = False,
|
|
46
75
|
can_build: bool = False,
|
|
47
76
|
can_restart: bool = False,
|
|
77
|
+
can_pull: bool = False,
|
|
48
78
|
status: str = "on",
|
|
49
79
|
publish_policy: PublishPolicy = PublishPolicy.HOMEASSISTANT,
|
|
50
80
|
update_type: str | None = "Update",
|
|
51
81
|
update_policy: UpdatePolicy = UpdatePolicy.PASSIVE,
|
|
52
82
|
version_policy: VersionPolicy = VersionPolicy.AUTO,
|
|
53
|
-
|
|
54
|
-
release_summary: str | None = None,
|
|
83
|
+
version_basis: str | None = None,
|
|
55
84
|
title_template: str = "{discovery.update_type} for {discovery.name} on {discovery.node}",
|
|
56
85
|
device_icon: str | None = None,
|
|
57
86
|
custom: dict[str, Any] | None = None,
|
|
58
|
-
features: list[str] | None = None,
|
|
59
87
|
throttled: bool = False,
|
|
60
88
|
previous: "Discovery|None" = None,
|
|
89
|
+
release_detail: ReleaseDetail | None = None,
|
|
90
|
+
installation_detail: DiscoveryInstallationDetail | None = None,
|
|
91
|
+
current_detail: DiscoveryArtefactDetail | None = None,
|
|
92
|
+
latest_detail: DiscoveryArtefactDetail | None = None,
|
|
61
93
|
) -> None:
|
|
62
94
|
self.provider: ReleaseProvider = provider
|
|
63
95
|
self.source_type: str = provider.source_type
|
|
@@ -67,11 +99,9 @@ class Discovery:
|
|
|
67
99
|
self.entity_picture_url: str | None = entity_picture_url
|
|
68
100
|
self.current_version: str | None = current_version
|
|
69
101
|
self.latest_version: str | None = latest_version
|
|
70
|
-
self.
|
|
102
|
+
self.can_pull: bool = can_pull
|
|
71
103
|
self.can_build: bool = can_build
|
|
72
104
|
self.can_restart: bool = can_restart
|
|
73
|
-
self.release_url: str | None = release_url
|
|
74
|
-
self.release_summary: str | None = release_summary
|
|
75
105
|
self.title_template: str | None = title_template
|
|
76
106
|
self.device_icon: str | None = device_icon
|
|
77
107
|
self.update_type: str | None = update_type
|
|
@@ -79,13 +109,18 @@ class Discovery:
|
|
|
79
109
|
self.publish_policy: PublishPolicy = publish_policy
|
|
80
110
|
self.update_policy: UpdatePolicy = update_policy
|
|
81
111
|
self.version_policy: VersionPolicy = version_policy
|
|
112
|
+
self.version_basis: str | None = version_basis
|
|
82
113
|
self.update_last_attempt: float | None = None
|
|
83
114
|
self.custom: dict[str, Any] = custom or {}
|
|
84
|
-
self.features: list[str] = features or []
|
|
85
115
|
self.throttled: bool = throttled
|
|
86
116
|
self.scan_count: int
|
|
87
117
|
self.first_timestamp: float
|
|
88
118
|
self.last_timestamp: float = time.time()
|
|
119
|
+
self.check_timestamp: float | None = time.time()
|
|
120
|
+
self.release_detail: ReleaseDetail | None = release_detail
|
|
121
|
+
self.current_detail: DiscoveryArtefactDetail | None = current_detail
|
|
122
|
+
self.latest_detail: DiscoveryArtefactDetail | None = latest_detail
|
|
123
|
+
self.installation_detail: DiscoveryInstallationDetail | None = installation_detail
|
|
89
124
|
|
|
90
125
|
if previous:
|
|
91
126
|
self.update_last_attempt = previous.update_last_attempt
|
|
@@ -94,6 +129,11 @@ class Discovery:
|
|
|
94
129
|
else:
|
|
95
130
|
self.first_timestamp = time.time()
|
|
96
131
|
self.scan_count = 1
|
|
132
|
+
if throttled and previous:
|
|
133
|
+
# roll forward last non-throttled check
|
|
134
|
+
self.check_timestamp = previous.check_timestamp
|
|
135
|
+
elif not throttled:
|
|
136
|
+
self.check_timestamp = time.time()
|
|
97
137
|
|
|
98
138
|
def __repr__(self) -> str:
|
|
99
139
|
"""Build a custom string representation"""
|
|
@@ -108,6 +148,21 @@ class Discovery:
|
|
|
108
148
|
dump = {k: stringify(v) for k, v in self.__dict__.items()}
|
|
109
149
|
return json.dumps(dump)
|
|
110
150
|
|
|
151
|
+
@property
|
|
152
|
+
def can_update(self) -> bool:
|
|
153
|
+
return self.can_pull or self.can_build or self.can_restart
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def features(self) -> list[str]:
|
|
157
|
+
results = []
|
|
158
|
+
if self.can_update:
|
|
159
|
+
# public install-neutral capabilities and Home Assistant features
|
|
160
|
+
results.append("INSTALL")
|
|
161
|
+
results.append("PROGRESS")
|
|
162
|
+
if self.release_detail and self.release_detail.notes_url:
|
|
163
|
+
results.append("RELEASE_NOTES")
|
|
164
|
+
return results
|
|
165
|
+
|
|
111
166
|
@property
|
|
112
167
|
def title(self) -> str:
|
|
113
168
|
if self.title_template:
|
|
@@ -115,7 +170,7 @@ class Discovery:
|
|
|
115
170
|
return self.name
|
|
116
171
|
|
|
117
172
|
def as_dict(self) -> dict[str, str | list | dict | bool | int | None]:
|
|
118
|
-
|
|
173
|
+
results: dict[str, str | list | dict | bool | int | None] = {
|
|
119
174
|
"name": self.name,
|
|
120
175
|
"node": self.node,
|
|
121
176
|
"provider": {"source_type": self.provider.source_type},
|
|
@@ -124,10 +179,8 @@ class Discovery:
|
|
|
124
179
|
"scan_count": self.scan_count,
|
|
125
180
|
"installed_version": self.current_version,
|
|
126
181
|
"latest_version": self.latest_version,
|
|
182
|
+
"version_basis": self.version_basis,
|
|
127
183
|
"title": self.title,
|
|
128
|
-
"release_summary": self.release_summary,
|
|
129
|
-
"release_url": self.release_url,
|
|
130
|
-
"entity_picture_url": self.entity_picture_url,
|
|
131
184
|
"can_update": self.can_update,
|
|
132
185
|
"can_build": self.can_build,
|
|
133
186
|
"can_restart": self.can_restart,
|
|
@@ -135,12 +188,20 @@ class Discovery:
|
|
|
135
188
|
"update_type": self.update_type,
|
|
136
189
|
"status": self.status,
|
|
137
190
|
"features": self.features,
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"
|
|
191
|
+
"entity_picture_url": self.entity_picture_url,
|
|
192
|
+
"update_policy": str(self.update_policy),
|
|
193
|
+
"publish_policy": str(self.publish_policy),
|
|
194
|
+
"version_policy": str(self.version_policy),
|
|
141
195
|
"update": {"last_attempt": timestamp(self.update_last_attempt), "in_progress": False},
|
|
142
|
-
self.
|
|
196
|
+
"installation_detail": self.installation_detail.as_dict() if self.installation_detail else None,
|
|
197
|
+
"current_detail": self.current_detail.as_dict() if self.current_detail else None,
|
|
198
|
+
"latest_detail": self.latest_detail.as_dict() if self.latest_detail else None,
|
|
143
199
|
}
|
|
200
|
+
if self.release_detail:
|
|
201
|
+
results["release"] = self.release_detail.as_dict() if self.release_detail else None
|
|
202
|
+
if self.custom:
|
|
203
|
+
results[self.source_type] = self.custom
|
|
204
|
+
return results
|
|
144
205
|
|
|
145
206
|
|
|
146
207
|
class ReleaseProvider:
|
|
@@ -188,59 +249,3 @@ class ReleaseProvider:
|
|
|
188
249
|
@abstractmethod
|
|
189
250
|
def resolve(self, discovery_name: str) -> Discovery | None:
|
|
190
251
|
"""Resolve a discovered component by name"""
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
class Selection:
|
|
194
|
-
def __init__(self, selector: Selector, value: str | None) -> None:
|
|
195
|
-
self.result: bool = True
|
|
196
|
-
self.matched: str | None = None
|
|
197
|
-
if value is None:
|
|
198
|
-
self.result = selector.include is None
|
|
199
|
-
return
|
|
200
|
-
if selector.exclude is not None:
|
|
201
|
-
self.result = True
|
|
202
|
-
if any(re.search(pat, value) for pat in selector.exclude):
|
|
203
|
-
self.matched = value
|
|
204
|
-
self.result = False
|
|
205
|
-
if selector.include is not None:
|
|
206
|
-
self.result = False
|
|
207
|
-
if any(re.search(pat, value) for pat in selector.include):
|
|
208
|
-
self.matched = value
|
|
209
|
-
self.result = True
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
VERSION_RE = r"[vV]?[0-9]+(\.[0-9]+)*"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def select_version(
|
|
216
|
-
version_policy: VersionPolicy,
|
|
217
|
-
version: str | None,
|
|
218
|
-
digest: str | None,
|
|
219
|
-
other_version: str | None = None,
|
|
220
|
-
other_digest: str | None = None,
|
|
221
|
-
) -> str:
|
|
222
|
-
if version_policy == VersionPolicy.VERSION and version:
|
|
223
|
-
return version
|
|
224
|
-
if version_policy == VersionPolicy.DIGEST and digest and digest != NO_KNOWN_IMAGE:
|
|
225
|
-
return digest
|
|
226
|
-
if version_policy == VersionPolicy.VERSION_DIGEST and version and digest and digest != NO_KNOWN_IMAGE:
|
|
227
|
-
return f"{version} ({digest})"
|
|
228
|
-
# AUTO or fallback
|
|
229
|
-
if version_policy == VersionPolicy.AUTO and version and re.match(VERSION_RE, version or ""):
|
|
230
|
-
# Smells like semver
|
|
231
|
-
if other_version is None and other_digest is None:
|
|
232
|
-
return version
|
|
233
|
-
if re.match(VERSION_RE, other_version or "") and (
|
|
234
|
-
(version == other_version and digest == other_digest) or (version != other_version and digest != other_digest)
|
|
235
|
-
):
|
|
236
|
-
# Only semver if versions and digest consistently same or different
|
|
237
|
-
return version
|
|
238
|
-
|
|
239
|
-
if version and digest and digest != NO_KNOWN_IMAGE:
|
|
240
|
-
return f"{version}:{digest}"
|
|
241
|
-
if version:
|
|
242
|
-
return version
|
|
243
|
-
if digest and digest != NO_KNOWN_IMAGE:
|
|
244
|
-
return digest
|
|
245
|
-
|
|
246
|
-
return other_version or other_version or NO_KNOWN_IMAGE
|
updates2mqtt/mqtt.py
CHANGED
|
@@ -203,6 +203,7 @@ class MqttPublisher:
|
|
|
203
203
|
async def execute_command(
|
|
204
204
|
self, msg: MQTTMessage | LocalMessage, on_update_start: Callable, on_update_end: Callable
|
|
205
205
|
) -> None:
|
|
206
|
+
# TODO: defer handling of commands where repository is throttled
|
|
206
207
|
logger = self.log.bind(topic=msg.topic, payload=msg.payload)
|
|
207
208
|
comp_name: str | None = None
|
|
208
209
|
command: str | None = None
|
|
@@ -235,6 +236,8 @@ class MqttPublisher:
|
|
|
235
236
|
updated = provider.command(comp_name, command, on_update_start, on_update_end)
|
|
236
237
|
discovery = provider.resolve(comp_name)
|
|
237
238
|
if updated and discovery:
|
|
239
|
+
if discovery.publish_policy == PublishPolicy.HOMEASSISTANT and self.hass_cfg.discovery.enabled:
|
|
240
|
+
self.publish_hass_config(discovery)
|
|
238
241
|
if discovery.publish_policy in (PublishPolicy.HOMEASSISTANT, PublishPolicy.MQTT):
|
|
239
242
|
self.publish_discovery(discovery)
|
|
240
243
|
if discovery.publish_policy == PublishPolicy.HOMEASSISTANT:
|
|
@@ -337,6 +340,8 @@ class MqttPublisher:
|
|
|
337
340
|
if discovery.publish_policy != PublishPolicy.HOMEASSISTANT:
|
|
338
341
|
return
|
|
339
342
|
object_id = f"{discovery.source_type}_{self.node_cfg.name}_{discovery.name}"
|
|
343
|
+
self.log.debug("HASS Config: %s", object_id)
|
|
344
|
+
|
|
340
345
|
self.publish(
|
|
341
346
|
self.config_topic(discovery),
|
|
342
347
|
hass_format_config(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: updates2mqtt
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.2
|
|
4
4
|
Summary: System update and docker image notification and execution over MQTT
|
|
5
5
|
Keywords: mqtt,docker,oci,container,updates,automation,home-assistant,homeassistant,selfhosting
|
|
6
6
|
Author: jey burrows
|
|
@@ -18,14 +18,14 @@ Classifier: Intended Audience :: System Administrators
|
|
|
18
18
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
19
19
|
Classifier: Typing :: Typed
|
|
20
20
|
Classifier: Programming Language :: Python
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
22
|
Requires-Dist: docker>=7.1.0
|
|
23
23
|
Requires-Dist: paho-mqtt>=2.1.0
|
|
24
24
|
Requires-Dist: omegaconf>=2.3.0
|
|
25
25
|
Requires-Dist: structlog>=25.4.0
|
|
26
26
|
Requires-Dist: rich>=14.0.0
|
|
27
27
|
Requires-Dist: httpx>=0.28.1
|
|
28
|
-
Requires-Dist: hishel[httpx]>=
|
|
28
|
+
Requires-Dist: hishel[httpx]>=1.1.0
|
|
29
29
|
Requires-Dist: usingversion>=0.1.2
|
|
30
30
|
Requires-Dist: tzlocal>=5.3.1
|
|
31
31
|
Requires-Python: >=3.13
|
|
@@ -61,7 +61,7 @@ Description-Content-Type: text/markdown
|
|
|
61
61
|
|
|
62
62
|
Let Home Assistant tell you about new updates to Docker images for your containers.
|
|
63
63
|
|
|
64
|
-

|
|
64
|
+
{width=300}
|
|
65
65
|
|
|
66
66
|
Read the release notes, and optionally click *Update* to trigger a Docker *pull* (or optionally *build*) and *update*.
|
|
67
67
|
|
|
@@ -72,7 +72,8 @@ Read the release notes, and optionally click *Update* to trigger a Docker *pull*
|
|
|
72
72
|
|
|
73
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.
|
|
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))
|
|
75
|
+
Currently only Docker containers are supported, either via an image registry check (using either v1 Docker APIs or the OCI v2 API), or a git repo for source (see [Local Builds](local_builds.md)), with specific handling for Docker, Github Container Registry, Gitlab, Codeberg, Microsoft Container Registry and LinuxServer Registry, with adaptive behaviour to cope with most
|
|
76
|
+
others. The design is modular, so other update sources can be added, at least for notification. The next anticipated is **apt** for Debian based systems.
|
|
76
77
|
|
|
77
78
|
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.
|
|
78
79
|
|
|
@@ -81,7 +82,7 @@ To get started, read the [Installation](installation.md) and [Configuration](con
|
|
|
81
82
|
For a quick spin, try this:
|
|
82
83
|
|
|
83
84
|
```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:
|
|
85
|
+
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:latest
|
|
85
86
|
```
|
|
86
87
|
|
|
87
88
|
or without Docker, using [uv](https://docs.astral.sh/uv/)
|
|
@@ -159,6 +160,8 @@ The following environment variables can be used to configure containers for `upd
|
|
|
159
160
|
| `UPD2MQTT_GIT_REPO_PATH` | Relative path to a local git repo if the image is built locally. | |
|
|
160
161
|
| `UPD2MQTT_IGNORE` | If set to `True`, the container will be ignored by Updates2MQTT. | False |
|
|
161
162
|
| |
|
|
163
|
+
| `UPD2MQTT_VERSION_POLICY` | Change how version derived from container label or image hash, `Version`,`Digest`,`Version_Digest` with default of `Auto`|
|
|
164
|
+
| `UPD2MQTT_REGISTRY_TOKEN` | Access token for authentication to container distribution API, as alternative to making a call to `token` service |
|
|
162
165
|
|
|
163
166
|
### Docker Labels
|
|
164
167
|
|
|
@@ -171,6 +174,9 @@ Alternatively, use Docker labels
|
|
|
171
174
|
| `updates2mqtt.relnotes` | `UPD2MQTT_RELNOTES` |
|
|
172
175
|
| `updates2mqtt.git_repo_path` | `UPD2MQTT_GIT_REPO_PATH` |
|
|
173
176
|
| `updates2mqtt.ignore` | `UPD2MQTT_IGNORE` |
|
|
177
|
+
| `updates2mqtt.version_policy` | `UPD2MQTT_VERSION_POLICY` |
|
|
178
|
+
| `updates2mqtt.registry_token` | `UPD2MQTT_REGISTRY_TOKEN` |
|
|
179
|
+
|
|
174
180
|
|
|
175
181
|
|
|
176
182
|
```yaml title="Example Compose Snippet"
|
|
@@ -200,7 +206,7 @@ This component relies on several open source packages:
|
|
|
200
206
|
- [Eclipse Paho](https://eclipse.dev/paho/files/paho.mqtt.python/html/client.html) MQTT client
|
|
201
207
|
- [OmegaConf](https://omegaconf.readthedocs.io) for configuration and validation
|
|
202
208
|
- [structlog](https://www.structlog.org/en/stable/) for structured logging and [rich](https://rich.readthedocs.io/en/stable/) for better exception reporting
|
|
203
|
-
- [hishel](https://hishel.com/
|
|
209
|
+
- [hishel](https://hishel.com/) for caching metadata
|
|
204
210
|
- [httpx](https://www.python-httpx.org) for retrieving metadata
|
|
205
211
|
- The Astral [uv](https://docs.astral.sh/uv/) and [ruff](https://docs.astral.sh/ruff/) tools for development and build
|
|
206
212
|
- [pytest](https://docs.pytest.org/en/stable/) and supporting add-ins for automated testing
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
updates2mqtt/__init__.py,sha256=gnmHrLOSYc-N1-c5VG46OpNpoXEybKzYhEvFMm955P8,237
|
|
2
|
+
updates2mqtt/__main__.py,sha256=HBF00oH5fhS33sI_CdbxNlaUvbIzuuGxwnRYdhHqx0M,194
|
|
3
|
+
updates2mqtt/app.py,sha256=4OOzVTuOw5Zxrm6zppRG6kq7x6bOY6S0h44yRnoYoVk,9651
|
|
4
|
+
updates2mqtt/cli.py,sha256=1ntGaJc8rOv8uU5l5oOCs80yey8A--CkHGPFYND0A6U,5237
|
|
5
|
+
updates2mqtt/config.py,sha256=Yfr5tHTVj4Tl-Zpmx6UZ4HBOOFvdoIYXi91bUVgl8E0,7243
|
|
6
|
+
updates2mqtt/hass_formatter.py,sha256=k0aLGg-7wI_C4TixhY-L-iz7n0QCKQ_Pvv37hSp22ww,2779
|
|
7
|
+
updates2mqtt/helpers.py,sha256=fGGBA8JrneAji0AdqyA2waivV9Jq_rXB-CT6TzIFNZ8,9282
|
|
8
|
+
updates2mqtt/integrations/__init__.py,sha256=KmNTUxvVWvqI7rl4I0xZg7XaCmcMS2O4OSv-ClsWM4Q,109
|
|
9
|
+
updates2mqtt/integrations/docker.py,sha256=848AbaNiRGdIRi0nG9-_3JePBHy5d48ETDXilIf_emM,30171
|
|
10
|
+
updates2mqtt/integrations/docker_enrich.py,sha256=1szsw7JpCWQcAG-rRp0KJ3Yj4MgE28C_RCKZngbnPv0,39474
|
|
11
|
+
updates2mqtt/integrations/git_utils.py,sha256=AnMiVW-noaBQ-17FeIl93jwpTSzvr70nIDEcJN3D-gw,4356
|
|
12
|
+
updates2mqtt/model.py,sha256=Pfwy2nSAq6-_ACEhZRsCVXlahTbDd07Ej2o_81SPdxc,10093
|
|
13
|
+
updates2mqtt/mqtt.py,sha256=EYsWKGKzmwf3VIjQmf_oI2C962k3QTWJKXRnLC9kzEU,16687
|
|
14
|
+
updates2mqtt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
updates2mqtt-1.7.2.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
|
|
16
|
+
updates2mqtt-1.7.2.dist-info/entry_points.txt,sha256=qtMKoTPaodbFC3YG7MLElWDjl7CfJdbrxxZyH6Bua8E,83
|
|
17
|
+
updates2mqtt-1.7.2.dist-info/METADATA,sha256=7FEYhqFY1T77JJx54AsJ5GmtJDVydCHQm7g_IiMwraA,12131
|
|
18
|
+
updates2mqtt-1.7.2.dist-info/RECORD,,
|
|
@@ -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=4OOzVTuOw5Zxrm6zppRG6kq7x6bOY6S0h44yRnoYoVk,9651
|
|
4
|
-
updates2mqtt/config.py,sha256=Hulk-zynpWq6JUv_ftEY5tmY-Vr8tQU9vV8nuhKXmJ4,5936
|
|
5
|
-
updates2mqtt/hass_formatter.py,sha256=xRm_iJiHU02v4KxNwps3V3g4dl_A7wNvQpLHCODzZlM,2690
|
|
6
|
-
updates2mqtt/integrations/__init__.py,sha256=KmNTUxvVWvqI7rl4I0xZg7XaCmcMS2O4OSv-ClsWM4Q,109
|
|
7
|
-
updates2mqtt/integrations/docker.py,sha256=GBYmzfzXuoI6j9GGIiDdq4KnR4677RCgzfxF6r4vTRM,28099
|
|
8
|
-
updates2mqtt/integrations/docker_enrich.py,sha256=wdJOOvZw7gG5ncHy6BqeIc9vPhR94YJYa9dumLmh8SY,15279
|
|
9
|
-
updates2mqtt/integrations/git_utils.py,sha256=ODCKecWnom1NEKsmDZ2vFmYfnVGtWUgq7svVGcOtSaU,4369
|
|
10
|
-
updates2mqtt/model.py,sha256=MmWzHq8ibXYRVMLmATrmhMp05q3neAIkP8Q1OkEepBs,9264
|
|
11
|
-
updates2mqtt/mqtt.py,sha256=KC8VYQ_OYiCoFehLT90dS5n8ZOZfffThapmQ29kyifE,16384
|
|
12
|
-
updates2mqtt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
updates2mqtt-1.7.0.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
|
|
14
|
-
updates2mqtt-1.7.0.dist-info/entry_points.txt,sha256=Nt1kQQfJ1M2RvcRUnVxe3KCMiX8puHPqz-D7BwqV1L8,55
|
|
15
|
-
updates2mqtt-1.7.0.dist-info/METADATA,sha256=POaF66x0AbR8qLJFyR3tDv27xRIbAu0Msyt2_o26TGU,11363
|
|
16
|
-
updates2mqtt-1.7.0.dist-info/RECORD,,
|
|
File without changes
|