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.
@@ -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 git_local_version(repo_path: Path, git_path: Path) -> str | None:
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="git_local_version", path=repo_path, version=result.stdout.strip())
60
- return f"git:{result.stdout.strip()}"[:19]
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="git_local_version")
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="git_local_version",
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 NO_KNOWN_IMAGE, NodeConfig, PublishPolicy, Selector, UpdatePolicy
10
+ from updates2mqtt.config import NodeConfig, PublishPolicy, UpdatePolicy, VersionPolicy
11
+ from updates2mqtt.helpers import timestamp
15
12
 
16
13
 
17
- def timestamp(time_value: float | None) -> str | None:
18
- if time_value is None:
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
- class VersionPolicy(StrEnum):
27
- AUTO = "AUTO"
28
- VERSION = "VERSION"
29
- DIGEST = "DIGEST"
30
- VERSION_DIGEST = "VERSION_DIGEST"
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
- release_url: str | None = None,
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.can_update: bool = can_update
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
- return {
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
- "update_policy": self.update_policy,
139
- "publish_policy": self.publish_policy,
140
- "version_policy": self.version_policy,
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.source_type: self.custom,
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.0
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.13
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]>=0.1.4
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
- ![Example Home Assistant update page](images/ha_update_detail.png "Home Assistant Updates")
64
+ ![Example Home Assistant update page](images/ha_update_detail.png "Home Assistant Updates")![Example Home Assistant Release Notes](images/ha_release_notes.png "Home Assistant Release Notes"){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)). 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
+ 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:release
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/1.0/) for caching metadata
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,3 +1,4 @@
1
1
  [console_scripts]
2
+ cli = updates2mqtt.cli:main
2
3
  updates2mqtt = updates2mqtt.app:run
3
4
 
@@ -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,,