updates2mqtt 1.5.0__py3-none-any.whl → 1.5.1__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 +13 -6
- updates2mqtt/hass_formatter.py +6 -15
- updates2mqtt/integrations/docker.py +14 -3
- updates2mqtt/model.py +11 -1
- updates2mqtt/mqtt.py +0 -2
- {updates2mqtt-1.5.0.dist-info → updates2mqtt-1.5.1.dist-info}/METADATA +1 -1
- updates2mqtt-1.5.1.dist-info/RECORD +16 -0
- updates2mqtt-1.5.0.dist-info/RECORD +0 -16
- {updates2mqtt-1.5.0.dist-info → updates2mqtt-1.5.1.dist-info}/WHEEL +0 -0
- {updates2mqtt-1.5.0.dist-info → updates2mqtt-1.5.1.dist-info}/entry_points.txt +0 -0
- {updates2mqtt-1.5.0.dist-info → updates2mqtt-1.5.1.dist-info}/licenses/LICENSE +0 -0
updates2mqtt/config.py
CHANGED
|
@@ -25,7 +25,7 @@ class MqttConfig:
|
|
|
25
25
|
password: str = f"${{oc.env:MQTT_PASS,{MISSING}}}"
|
|
26
26
|
port: int = "${oc.decode:${oc.env:MQTT_PORT,1883}}" # type: ignore[assignment]
|
|
27
27
|
topic_root: str = "updates2mqtt"
|
|
28
|
-
protocol: str = "3.11"
|
|
28
|
+
protocol: str = "${oc.env:MQTT_VERSION,3.11}"
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@dataclass
|
|
@@ -80,12 +80,12 @@ class NodeConfig:
|
|
|
80
80
|
|
|
81
81
|
@dataclass
|
|
82
82
|
class LogConfig:
|
|
83
|
-
level: LogLevel =
|
|
83
|
+
level: LogLevel = "${oc.decode:${oc.env:U2M_LOG_LEVEL,INFO}}" # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
@dataclass
|
|
87
87
|
class Config:
|
|
88
|
-
log: LogConfig = field(default_factory=LogConfig)
|
|
88
|
+
log: LogConfig = field(default_factory=LogConfig) # pyright: ignore[reportArgumentType, reportCallIssue]
|
|
89
89
|
node: NodeConfig = field(default_factory=NodeConfig)
|
|
90
90
|
mqtt: MqttConfig = field(default_factory=MqttConfig) # pyright: ignore[reportArgumentType, reportCallIssue]
|
|
91
91
|
homeassistant: HomeAssistantConfig = field(default_factory=HomeAssistantConfig)
|
|
@@ -132,22 +132,29 @@ def load_package_info(pkginfo_file_path: Path) -> dict[str, PackageUpdateInfo]:
|
|
|
132
132
|
raise
|
|
133
133
|
|
|
134
134
|
|
|
135
|
+
def is_autogen_config() -> bool:
|
|
136
|
+
env_var: str | None = os.environ.get("U2M_AUTOGEN_CONFIG")
|
|
137
|
+
return not (env_var and env_var.lower() in ("no", "0", "false"))
|
|
138
|
+
|
|
139
|
+
|
|
135
140
|
def load_app_config(conf_file_path: Path, return_invalid: bool = False) -> Config | None:
|
|
136
141
|
base_cfg: DictConfig = OmegaConf.structured(Config)
|
|
137
142
|
if conf_file_path.exists():
|
|
138
143
|
cfg: DictConfig = typing.cast("DictConfig", OmegaConf.merge(base_cfg, OmegaConf.load(conf_file_path)))
|
|
139
|
-
|
|
144
|
+
elif is_autogen_config():
|
|
140
145
|
if not conf_file_path.parent.exists():
|
|
141
146
|
try:
|
|
142
147
|
log.debug(f"Creating config directory {conf_file_path.parent} if not already present")
|
|
143
148
|
conf_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
144
149
|
except Exception:
|
|
145
|
-
log.
|
|
150
|
+
log.warning("Unable to create config directory", path=conf_file_path.parent)
|
|
146
151
|
try:
|
|
147
152
|
conf_file_path.write_text(OmegaConf.to_yaml(base_cfg))
|
|
148
153
|
log.info(f"Auto-generated a new config file at {conf_file_path}")
|
|
149
154
|
except Exception:
|
|
150
|
-
log.
|
|
155
|
+
log.warning("Unable to write config file", path=conf_file_path)
|
|
156
|
+
cfg = base_cfg
|
|
157
|
+
else:
|
|
151
158
|
cfg = base_cfg
|
|
152
159
|
|
|
153
160
|
try:
|
updates2mqtt/hass_formatter.py
CHANGED
|
@@ -21,7 +21,6 @@ HASS_UPDATE_SCHEMA = [
|
|
|
21
21
|
def hass_format_config(
|
|
22
22
|
discovery: Discovery,
|
|
23
23
|
object_id: str,
|
|
24
|
-
node_name: str,
|
|
25
24
|
state_topic: str,
|
|
26
25
|
command_topic: str | None,
|
|
27
26
|
force_command_topic: bool | None,
|
|
@@ -29,13 +28,8 @@ def hass_format_config(
|
|
|
29
28
|
area: str | None = None,
|
|
30
29
|
session: str | None = None,
|
|
31
30
|
) -> dict[str, Any]:
|
|
32
|
-
if device_creation:
|
|
33
|
-
# avoid duplication, since Home Assistant will concatenate device and entity name on update
|
|
34
|
-
name: str = f"{discovery.name} {discovery.source_type}"
|
|
35
|
-
else:
|
|
36
|
-
name = f"{discovery.name} {discovery.source_type} on {node_name}"
|
|
37
31
|
config: dict[str, Any] = {
|
|
38
|
-
"name":
|
|
32
|
+
"name": discovery.title,
|
|
39
33
|
"device_class": None, # not firmware, so defaults to null
|
|
40
34
|
"unique_id": object_id,
|
|
41
35
|
"state_topic": state_topic,
|
|
@@ -46,7 +40,7 @@ def hass_format_config(
|
|
|
46
40
|
"can_restart": discovery.can_restart,
|
|
47
41
|
"update_policy": discovery.update_policy,
|
|
48
42
|
"origin": {
|
|
49
|
-
"name": f"{
|
|
43
|
+
"name": f"{discovery.node} updates2mqtt",
|
|
50
44
|
"sw_version": updates2mqtt.version, # pyright: ignore[reportAttributeAccessIssue]
|
|
51
45
|
"support_url": "https://github.com/rhizomatics/updates2mqtt/issues",
|
|
52
46
|
},
|
|
@@ -57,10 +51,10 @@ def hass_format_config(
|
|
|
57
51
|
config["icon"] = discovery.device_icon
|
|
58
52
|
if device_creation:
|
|
59
53
|
config["device"] = {
|
|
60
|
-
"name": f"{
|
|
54
|
+
"name": f"{discovery.node} updates2mqtt",
|
|
61
55
|
"sw_version": updates2mqtt.version, # pyright: ignore[reportAttributeAccessIssue]
|
|
62
56
|
"manufacturer": "rhizomatics",
|
|
63
|
-
"identifiers": [f"{
|
|
57
|
+
"identifiers": [f"{discovery.node}.updates2mqtt"],
|
|
64
58
|
}
|
|
65
59
|
if area:
|
|
66
60
|
config["device"]["suggested_area"] = area
|
|
@@ -74,14 +68,11 @@ def hass_format_config(
|
|
|
74
68
|
return config
|
|
75
69
|
|
|
76
70
|
|
|
77
|
-
def hass_format_state(discovery: Discovery,
|
|
78
|
-
title: str = (
|
|
79
|
-
discovery.title_template.format(name=discovery.name, node=node_name) if discovery.title_template else discovery.name
|
|
80
|
-
)
|
|
71
|
+
def hass_format_state(discovery: Discovery, session: str, in_progress: bool = False) -> dict[str, Any]: # noqa: ARG001
|
|
81
72
|
state = {
|
|
82
73
|
"installed_version": discovery.current_version,
|
|
83
74
|
"latest_version": discovery.latest_version,
|
|
84
|
-
"title": title,
|
|
75
|
+
"title": discovery.title,
|
|
85
76
|
"in_progress": in_progress,
|
|
86
77
|
}
|
|
87
78
|
if discovery.release_summary:
|
|
@@ -101,18 +101,22 @@ class DockerProvider(ReleaseProvider):
|
|
|
101
101
|
if not cwd or not Path(cwd).is_dir():
|
|
102
102
|
logger.warn("Invalid compose path, skipped %s", command)
|
|
103
103
|
return False
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
cmd: str = "docker-compose" if self.cfg.compose_version == "v1" else "docker compose"
|
|
106
|
+
logger.info(f"Executing {cmd} {command} {args} {service}")
|
|
106
107
|
cmd = cmd + " " + command.value
|
|
107
108
|
if args:
|
|
108
109
|
cmd = cmd + " " + args
|
|
109
110
|
if service:
|
|
110
111
|
cmd = cmd + " " + service
|
|
111
112
|
|
|
112
|
-
proc = subprocess.run(cmd, check=False, shell=True, cwd=cwd)
|
|
113
|
+
proc: subprocess.CompletedProcess[str] = subprocess.run(cmd, check=False, shell=True, cwd=cwd, text=True)
|
|
113
114
|
if proc.returncode == 0:
|
|
114
115
|
logger.info(f"{command} via compose successful")
|
|
115
116
|
return True
|
|
117
|
+
if proc.stderr and "unknown command: docker compose" in proc.stderr:
|
|
118
|
+
logger.warning("docker compose set to wrong version, seems like v1 installed")
|
|
119
|
+
self.cfg.compose_version = "v1"
|
|
116
120
|
logger.warn(
|
|
117
121
|
f"{command} failed: %s",
|
|
118
122
|
proc.returncode,
|
|
@@ -275,6 +279,12 @@ class DockerProvider(ReleaseProvider):
|
|
|
275
279
|
logger.info(f"Update not available, can_pull:{can_pull}, can_build:{can_build},can_restart{can_restart}")
|
|
276
280
|
if relnotes_url:
|
|
277
281
|
features.append("RELEASE_NOTES")
|
|
282
|
+
if can_pull:
|
|
283
|
+
update_type: str = "Docker Image"
|
|
284
|
+
elif can_build:
|
|
285
|
+
update_type = "Docker Build"
|
|
286
|
+
else:
|
|
287
|
+
update_type = "Unavailable"
|
|
278
288
|
custom["can_pull"] = can_pull
|
|
279
289
|
|
|
280
290
|
logger.debug("Analyze generated discovery", discovery_name=c.name, current_version=local_version)
|
|
@@ -282,15 +292,16 @@ class DockerProvider(ReleaseProvider):
|
|
|
282
292
|
self,
|
|
283
293
|
c.name,
|
|
284
294
|
session,
|
|
295
|
+
node=self.node_cfg.name,
|
|
285
296
|
entity_picture_url=picture_url,
|
|
286
297
|
release_url=relnotes_url,
|
|
287
298
|
current_version=local_version,
|
|
288
299
|
update_policy=update_policy,
|
|
289
300
|
update_last_attempt=(original_discovery and original_discovery.update_last_attempt) or None,
|
|
290
301
|
latest_version=latest_version if latest_version != NO_KNOWN_IMAGE else local_version,
|
|
291
|
-
title_template="Docker image update for {name} on {node}",
|
|
292
302
|
device_icon=self.cfg.device_icon,
|
|
293
303
|
can_update=can_update,
|
|
304
|
+
update_type=update_type,
|
|
294
305
|
can_build=can_build,
|
|
295
306
|
can_restart=can_restart,
|
|
296
307
|
status=(c.status == "running" and "on") or "off",
|
updates2mqtt/model.py
CHANGED
|
@@ -14,6 +14,7 @@ class Discovery:
|
|
|
14
14
|
provider: "ReleaseProvider",
|
|
15
15
|
name: str,
|
|
16
16
|
session: str,
|
|
17
|
+
node: str,
|
|
17
18
|
entity_picture_url: str | None = None,
|
|
18
19
|
current_version: str | None = None,
|
|
19
20
|
latest_version: str | None = None,
|
|
@@ -21,11 +22,12 @@ class Discovery:
|
|
|
21
22
|
can_build: bool = False,
|
|
22
23
|
can_restart: bool = False,
|
|
23
24
|
status: str = "on",
|
|
25
|
+
update_type: str | None = "Update",
|
|
24
26
|
update_policy: str | None = None,
|
|
25
27
|
update_last_attempt: float | None = None,
|
|
26
28
|
release_url: str | None = None,
|
|
27
29
|
release_summary: str | None = None,
|
|
28
|
-
title_template: str = "
|
|
30
|
+
title_template: str = "{discovery.update_type} for {discovery.name} on {discovery.node}",
|
|
29
31
|
device_icon: str | None = None,
|
|
30
32
|
custom: dict[str, Any] | None = None,
|
|
31
33
|
features: list[str] | None = None,
|
|
@@ -34,6 +36,7 @@ class Discovery:
|
|
|
34
36
|
self.source_type: str = provider.source_type
|
|
35
37
|
self.session: str = session
|
|
36
38
|
self.name: str = name
|
|
39
|
+
self.node: str = node
|
|
37
40
|
self.entity_picture_url: str | None = entity_picture_url
|
|
38
41
|
self.current_version: str | None = current_version
|
|
39
42
|
self.latest_version: str | None = latest_version
|
|
@@ -44,6 +47,7 @@ class Discovery:
|
|
|
44
47
|
self.release_summary: str | None = release_summary
|
|
45
48
|
self.title_template: str | None = title_template
|
|
46
49
|
self.device_icon: str | None = device_icon
|
|
50
|
+
self.update_type: str | None = update_type
|
|
47
51
|
self.status: str = status
|
|
48
52
|
self.update_policy: str | None = update_policy
|
|
49
53
|
self.update_last_attempt: float | None = update_last_attempt
|
|
@@ -54,6 +58,12 @@ class Discovery:
|
|
|
54
58
|
"""Build a custom string representation"""
|
|
55
59
|
return f"Discovery('{self.name}','{self.source_type}',current={self.current_version},latest={self.latest_version})"
|
|
56
60
|
|
|
61
|
+
@property
|
|
62
|
+
def title(self) -> str:
|
|
63
|
+
if self.title_template:
|
|
64
|
+
return self.title_template.format(discovery=self)
|
|
65
|
+
return self.name
|
|
66
|
+
|
|
57
67
|
|
|
58
68
|
class ReleaseProvider:
|
|
59
69
|
"""Abstract base class for release providers, such as container scanners or package managers API calls"""
|
updates2mqtt/mqtt.py
CHANGED
|
@@ -309,7 +309,6 @@ class MqttPublisher:
|
|
|
309
309
|
self.state_topic(discovery),
|
|
310
310
|
hass_format_state(
|
|
311
311
|
discovery,
|
|
312
|
-
self.node_cfg.name,
|
|
313
312
|
discovery.session,
|
|
314
313
|
in_progress=in_progress,
|
|
315
314
|
),
|
|
@@ -322,7 +321,6 @@ class MqttPublisher:
|
|
|
322
321
|
hass_format_config(
|
|
323
322
|
discovery=discovery,
|
|
324
323
|
object_id=object_id,
|
|
325
|
-
node_name=self.node_cfg.name,
|
|
326
324
|
area=self.hass_cfg.area,
|
|
327
325
|
state_topic=self.state_topic(discovery),
|
|
328
326
|
command_topic=self.command_topic(discovery.provider),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: updates2mqtt
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.1
|
|
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
|
|
@@ -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=pYcNprv7_htqD6wiXX5j24FhPglEeFNdqMfPclq5SfU,8887
|
|
4
|
+
updates2mqtt/config.py,sha256=nlAszykZa7t9nu99RB8g_tIzQxmdfWyilTZJY3Fl4e4,6099
|
|
5
|
+
updates2mqtt/hass_formatter.py,sha256=bj6qpElMqt1DqKlxp4ZjwaCJAD-ed3xsq5ZOg4FbeC8,3216
|
|
6
|
+
updates2mqtt/model.py,sha256=KNsLflgWaRvGrNdq1Vy2QnDLfVSLPWLSt4gBXj9COa4,4170
|
|
7
|
+
updates2mqtt/mqtt.py,sha256=j7nlSka-LqRethZk_rc4Z8iOV7ey28TQFjSfrPeTjv4,14995
|
|
8
|
+
updates2mqtt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
updates2mqtt/integrations/__init__.py,sha256=KmNTUxvVWvqI7rl4I0xZg7XaCmcMS2O4OSv-ClsWM4Q,109
|
|
10
|
+
updates2mqtt/integrations/docker.py,sha256=Mi3tFjVDbCIsHBgH6UQ3yC5Y7HQk9JgEyH6PQuNJtFU,21263
|
|
11
|
+
updates2mqtt/integrations/git_utils.py,sha256=bPCmQiZpKpMcrGI7xAVmePXHFn8WwjcPNkf7xqDsGQA,2319
|
|
12
|
+
updates2mqtt-1.5.1.dist-info/METADATA,sha256=iPMKAgEFTFZ98pouv3SnTTStP74SEPBuV8jN3QbBpX8,9392
|
|
13
|
+
updates2mqtt-1.5.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
+
updates2mqtt-1.5.1.dist-info/entry_points.txt,sha256=Hc6NZ2dBevYSUKTJU6NOs8Mw7Vt0S-9lq5FuKb76NCc,54
|
|
15
|
+
updates2mqtt-1.5.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
+
updates2mqtt-1.5.1.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=pYcNprv7_htqD6wiXX5j24FhPglEeFNdqMfPclq5SfU,8887
|
|
4
|
-
updates2mqtt/config.py,sha256=TJakojUkPJ0xJ2aivJ9BIlDLw8glqJzTOsm1E__vJ9M,5706
|
|
5
|
-
updates2mqtt/hass_formatter.py,sha256=adopRRagQte7ok1ZBPcYBIkDAo9YLXcI2y_jtdNWZP8,3638
|
|
6
|
-
updates2mqtt/model.py,sha256=O6GQFhfQvwQDxlZScFHrpQgFNkUrUAeaGGP6AqNua78,3827
|
|
7
|
-
updates2mqtt/mqtt.py,sha256=yNpYlYdDr6S4ltLZqAbjUgxsJQZKzOfcFJoUr-fcNlI,15077
|
|
8
|
-
updates2mqtt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
updates2mqtt/integrations/__init__.py,sha256=KmNTUxvVWvqI7rl4I0xZg7XaCmcMS2O4OSv-ClsWM4Q,109
|
|
10
|
-
updates2mqtt/integrations/docker.py,sha256=dTbiIB2bjiGY46ovlkiB998ekhXX4M2KhvZqfzqV-Oo,20790
|
|
11
|
-
updates2mqtt/integrations/git_utils.py,sha256=bPCmQiZpKpMcrGI7xAVmePXHFn8WwjcPNkf7xqDsGQA,2319
|
|
12
|
-
updates2mqtt-1.5.0.dist-info/METADATA,sha256=oowFbgm4wTYWv3vlBTgiYe3prCI2UVTJztMT4GWLEN8,9392
|
|
13
|
-
updates2mqtt-1.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
-
updates2mqtt-1.5.0.dist-info/entry_points.txt,sha256=Hc6NZ2dBevYSUKTJU6NOs8Mw7Vt0S-9lq5FuKb76NCc,54
|
|
15
|
-
updates2mqtt-1.5.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
-
updates2mqtt-1.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|