updates2mqtt 1.5.0__tar.gz → 1.5.1__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.0 → updates2mqtt-1.5.1}/CHANGELOG.md +6 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/PKG-INFO +1 -1
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/conftest.py +2 -2
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/configuration.md +22 -2
- updates2mqtt-1.5.1/docs/examples/config_minimal.md +7 -0
- updates2mqtt-1.5.1/docs/examples/env.md +15 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/installation.md +1 -1
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/pyproject.toml +1 -1
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/config.py +13 -6
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/hass_formatter.py +6 -15
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/integrations/docker.py +14 -3
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/model.py +11 -1
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/mqtt.py +0 -2
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/tests/test_config.py +31 -1
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/tests/test_docker.py +1 -1
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/tests/test_hass_formatter.py +5 -8
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/tests/test_mqtt.py +20 -16
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/uv.lock +64 -64
- updates2mqtt-1.5.0/docs/examples/config_minimal.md +0 -7
- updates2mqtt-1.5.0/docs/examples/env.md +0 -7
- updates2mqtt-1.5.0/no_config.yaml +0 -33
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.dockerignore +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.github/dependabot.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.github/workflows/auto_assign_issue.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.github/workflows/auto_assign_pr.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.github/workflows/docker-publish.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.github/workflows/main.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.github/workflows/pypi-publish.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.github/workflows/python-package.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.gitignore +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.hintrc +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.pre-commit-config.yaml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.python-version +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/.safety-project.ini +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/Dockerfile +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/LICENSE +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/README.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/SECURITY.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/common_packages.yaml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/examples/config_maximal.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/examples/docker_compose.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/examples/index.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/home_assistant.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/images/ha_entities.png +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/images/ha_mqtt_discovery.png +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/images/ha_update_detail.png +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/images/ha_update_dialog.png +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/images/ha_update_page.png +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/images/logo-blank-256x256.png +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/images/updates2mqtt-dark-256x256.png +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/index.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/local_builds.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/robots.txt +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/docs/troubleshooting.md +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/examples/config.yaml.maximal +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/examples/config.yaml.minimal +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/examples/docker-compose.yaml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/mkdocs.yml +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/refresh-deps.sh +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/scripts/healthcheck.sh +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/__init__.py +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/__main__.py +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/app.py +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/integrations/__init__.py +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/integrations/git_utils.py +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/src/updates2mqtt/py.typed +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/tests/__init__.py +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/tests/test_app.py +0 -0
- {updates2mqtt-1.5.0 → updates2mqtt-1.5.1}/tests/test_git_utils.py +0 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.5.1
|
|
4
|
+
- `MQTT_VERSION` environment variable added, defaults to `3.11`
|
|
5
|
+
- `U2M_AUTOGEN_CONFIG` environment variable added to control auto-generation of config files and directories
|
|
6
|
+
- `U2M_LOG_LEVEL` environment variable added to set log level without config file
|
|
7
|
+
- Title generation for Docker images reverts to same whether HA device set or not
|
|
8
|
+
- Test added to ensure component always functions without a config file, if no env var present
|
|
3
9
|
## 1.5.0
|
|
4
10
|
- Target specific service on docker compose commands, where available from `com.docker.compose.service` label
|
|
5
11
|
- Log level in config is now an enum, and forced to be upper case
|
|
@@ -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
|
|
@@ -35,7 +35,7 @@ def app_with_mocked_external_dependencies(
|
|
|
35
35
|
|
|
36
36
|
@pytest.fixture
|
|
37
37
|
def mock_discoveries(mock_provider: ReleaseProvider) -> list[Discovery]:
|
|
38
|
-
return [Discovery(mock_provider, "thing-1", "test001", can_update=True)]
|
|
38
|
+
return [Discovery(mock_provider, "thing-1", "test001", "testbed01", can_update=True, update_type="TestRun")]
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
@pytest.fixture
|
|
@@ -71,7 +71,7 @@ def mock_provider() -> ReleaseProvider:
|
|
|
71
71
|
provider.source_type = "unit_test"
|
|
72
72
|
provider.command.return_value = True # type: ignore[attr-defined]
|
|
73
73
|
provider.resolve.return_value = Discovery( # type: ignore[attr-defined]
|
|
74
|
-
provider, "fooey", session="test-mqtt-123", current_version="v2", latest_version="v2"
|
|
74
|
+
provider, "fooey", session="test-mqtt-123", node="node002", current_version="v2", latest_version="v2"
|
|
75
75
|
)
|
|
76
76
|
provider.hass_state_format.return_value = {"fixture": "test_exec"} # type: ignore[attr-defined]
|
|
77
77
|
return provider
|
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
# Configuration
|
|
1
|
+
# Minimal Configuration
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The core configuration can be supplied by environment variables, everything else will default, either to fixed values built into Updates2MQTT, or in the case of the node name, taken from the operating system.
|
|
4
|
+
|
|
5
|
+
| Env Var | Default |
|
|
6
|
+
|---------------|--------------|
|
|
7
|
+
| MQTT_HOST | localhost |
|
|
8
|
+
| MQTT_PORT | 1883 |
|
|
9
|
+
| MQTT_USER | *NO DEFAULT* |
|
|
10
|
+
| MQTT_PASSWORD | *NO DEFAULT* |
|
|
11
|
+
| MQTT_VERSION | 3.11. |
|
|
12
|
+
| U2M_LOG_LEVEL | INFO |
|
|
13
|
+
|
|
14
|
+
Startup will fail if `MQTT_USER` and `MQTT_PASSWORD` are not defined some how.
|
|
15
|
+
|
|
16
|
+
The example [docker-compose.yaml](docker_compose.md) and [.env](env.md) demonstrate one way of doing this, or skip
|
|
17
|
+
the `.env` file and use an `environment` section in the Compose file.
|
|
18
|
+
|
|
19
|
+
# Configuration File
|
|
20
|
+
|
|
21
|
+
The configuration file is optional, and only needed if you have to override the defaults.
|
|
22
|
+
|
|
23
|
+
Create file `config.yaml` in `conf` directory. If the file is not present, a default file will be generated, and the parent director if necessary. If you don't want that to happen, then set `U2M_AUTOGEN_CONFIG=0`.
|
|
4
24
|
|
|
5
25
|
### Example configuration file
|
|
6
26
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Environment File
|
|
2
|
+
|
|
3
|
+
Example `.env` file for Docker Compose configuration to separately store the environment variables.
|
|
4
|
+
|
|
5
|
+
``` bash
|
|
6
|
+
# Example env file for docker-compose.yaml
|
|
7
|
+
# Used by both the app itself and the healthcheck script
|
|
8
|
+
|
|
9
|
+
# To use these, add to config.yaml like ${oc.env:MQTT_HOST}
|
|
10
|
+
|
|
11
|
+
MQTT_HOST=192.168.0.1
|
|
12
|
+
MQTT_PORT=1883
|
|
13
|
+
MQTT_USER=my_mqtt_user
|
|
14
|
+
MQTT_PASS=my_mqtt_secret
|
|
15
|
+
```
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
updates2mqtt prefers to be run inside a Docker container, though can run standalone, for example scripted via cron or systemd.
|
|
6
6
|
|
|
7
|
-
The only mandatory configuration is the MQTT broker host, user name and password, which can be set by environment
|
|
7
|
+
The only mandatory configuration is the MQTT broker host, user name and password, which can be set by environment variables, or the config file. The node name will be taken from the operating system if there's no config file. See [Configuration](configuration.md) for details.
|
|
8
8
|
|
|
9
9
|
### Docker
|
|
10
10
|
|
|
@@ -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:
|
|
@@ -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",
|
|
@@ -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"""
|
|
@@ -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,11 +1,14 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import tempfile
|
|
2
3
|
import uuid
|
|
4
|
+
from collections import namedtuple
|
|
3
5
|
from pathlib import Path
|
|
6
|
+
from unittest.mock import Mock, patch
|
|
4
7
|
|
|
5
8
|
import pytest
|
|
6
9
|
from omegaconf import OmegaConf
|
|
7
10
|
|
|
8
|
-
from updates2mqtt.config import MqttConfig, PackageUpdateInfo, load_app_config, load_package_info
|
|
11
|
+
from updates2mqtt.config import LogLevel, MqttConfig, PackageUpdateInfo, load_app_config, load_package_info
|
|
9
12
|
|
|
10
13
|
EXAMPLES_ROOT = "examples"
|
|
11
14
|
examples = [str(p.name) for p in Path(EXAMPLES_ROOT).iterdir() if p.name.startswith("config")]
|
|
@@ -57,6 +60,33 @@ def test_round_trip_config() -> None:
|
|
|
57
60
|
assert reloaded_config.mqtt.user == "myuser"
|
|
58
61
|
|
|
59
62
|
|
|
63
|
+
uname: type = namedtuple("uname", ["nodename"])
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@patch.dict(
|
|
67
|
+
os.environ,
|
|
68
|
+
{
|
|
69
|
+
"MQTT_HOST": "192.168.3.4",
|
|
70
|
+
"MQTT_PORT": "2883",
|
|
71
|
+
"MQTT_USER": "u2macct",
|
|
72
|
+
"MQTT_PASS": "toosecret123!",
|
|
73
|
+
"U2M_LOG_LEVEL": "WARNING",
|
|
74
|
+
"U2M_AUTOGEN_CONFIG": "0",
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
@patch("os.uname", Mock(return_value=uname("xunit003a")))
|
|
78
|
+
def test_env_only_config() -> None:
|
|
79
|
+
generated_config = load_app_config(Path("no_such_dir/no_such_file.yaml"))
|
|
80
|
+
assert generated_config is not None
|
|
81
|
+
assert generated_config.mqtt.host == "192.168.3.4"
|
|
82
|
+
assert generated_config.mqtt.port == 2883
|
|
83
|
+
assert generated_config.mqtt.user == "u2macct"
|
|
84
|
+
assert generated_config.mqtt.password == "toosecret123!" # noqa: S105
|
|
85
|
+
assert generated_config.node.name == "xunit003a"
|
|
86
|
+
assert generated_config.log.level == LogLevel.WARNING
|
|
87
|
+
assert not Path("no_such_dir").exists()
|
|
88
|
+
|
|
89
|
+
|
|
60
90
|
def test_package_config() -> None:
|
|
61
91
|
validated_pkg_info: dict[str, PackageUpdateInfo] = load_package_info(Path("common_packages.yaml"))
|
|
62
92
|
assert validated_pkg_info is not None
|
|
@@ -80,7 +80,7 @@ def test_discover_metadata(httpx_mock: HTTPXMock, mock_docker_client: DockerClie
|
|
|
80
80
|
def test_build(mock_docker_client: DockerClient, fake_process: FakeProcess, tmpdir: Path) -> None:
|
|
81
81
|
with patch("docker.from_env", return_value=mock_docker_client):
|
|
82
82
|
uut = mut.DockerProvider(mut.DockerConfig(discover_metadata={}), {}, mut.NodeConfig())
|
|
83
|
-
d = Discovery(uut, "build-test-dummy",
|
|
83
|
+
d = Discovery(uut, "build-test-dummy", "test-123", "node003")
|
|
84
84
|
fake_process.register("docker compose build", returncode=0)
|
|
85
85
|
assert uut.build(d, str(tmpdir))
|
|
86
86
|
fake_process.register("docker compose build", returncode=33)
|
|
@@ -8,7 +8,6 @@ def test_formatter_includes_device(mock_discoveries: list[Discovery], monkeypatc
|
|
|
8
8
|
msg = hass_format_config(
|
|
9
9
|
mock_discoveries[0],
|
|
10
10
|
"obj001",
|
|
11
|
-
"testbed01",
|
|
12
11
|
"state_topic_1",
|
|
13
12
|
"command_topic_1",
|
|
14
13
|
force_command_topic=False,
|
|
@@ -16,7 +15,7 @@ def test_formatter_includes_device(mock_discoveries: list[Discovery], monkeypatc
|
|
|
16
15
|
area="Basement",
|
|
17
16
|
)
|
|
18
17
|
assert msg == {
|
|
19
|
-
"name": "thing-1
|
|
18
|
+
"name": "TestRun for thing-1 on testbed01",
|
|
20
19
|
"unique_id": "obj001",
|
|
21
20
|
"update_policy": None,
|
|
22
21
|
"can_build": False,
|
|
@@ -45,11 +44,9 @@ def test_formatter_includes_device(mock_discoveries: list[Discovery], monkeypatc
|
|
|
45
44
|
|
|
46
45
|
def test_formatter_excludes_device(mock_discoveries: list[Discovery], monkeypatch) -> None: # noqa: ANN001
|
|
47
46
|
monkeypatch.setattr(updates2mqtt, "version", "3.0.0")
|
|
48
|
-
msg = hass_format_config(
|
|
49
|
-
mock_discoveries[0], "obj001", "testbed01", "state_topic_1", "command_topic_1", True, device_creation=False
|
|
50
|
-
)
|
|
47
|
+
msg = hass_format_config(mock_discoveries[0], "obj001", "state_topic_1", "command_topic_1", True, device_creation=False)
|
|
51
48
|
assert msg == {
|
|
52
|
-
"name": "thing-1
|
|
49
|
+
"name": "TestRun for thing-1 on testbed01",
|
|
53
50
|
"unique_id": "obj001",
|
|
54
51
|
"update_policy": None,
|
|
55
52
|
"can_build": False,
|
|
@@ -72,7 +69,7 @@ def test_formatter_excludes_device(mock_discoveries: list[Discovery], monkeypatc
|
|
|
72
69
|
def test_formatter_forces_command_topic(mock_discoveries: list[Discovery]) -> None:
|
|
73
70
|
discovery = mock_discoveries[0]
|
|
74
71
|
discovery.can_update = False
|
|
75
|
-
msg = hass_format_config(discovery, "obj001", "
|
|
72
|
+
msg = hass_format_config(discovery, "obj001", "state_topic_1", "command_topic_1", True)
|
|
76
73
|
assert msg["command_topic"] == "command_topic_1"
|
|
77
74
|
assert "payload_install" not in msg
|
|
78
75
|
|
|
@@ -80,6 +77,6 @@ def test_formatter_forces_command_topic(mock_discoveries: list[Discovery]) -> No
|
|
|
80
77
|
def test_formatter_no_update_suppresses_command_topic(mock_discoveries: list[Discovery]) -> None:
|
|
81
78
|
discovery = mock_discoveries[0]
|
|
82
79
|
discovery.can_update = False
|
|
83
|
-
msg = hass_format_config(discovery, "obj001", "
|
|
80
|
+
msg = hass_format_config(discovery, "obj001", "state_topic_1", "command_topic_1", False)
|
|
84
81
|
assert "command_topic" not in msg
|
|
85
82
|
assert "payload_install" not in msg
|
|
@@ -6,6 +6,7 @@ from unittest.mock import Mock, patch
|
|
|
6
6
|
|
|
7
7
|
import paho.mqtt.client
|
|
8
8
|
import pytest
|
|
9
|
+
from omegaconf import OmegaConf
|
|
9
10
|
from paho.mqtt.client import MQTTMessage
|
|
10
11
|
|
|
11
12
|
from updates2mqtt.config import HomeAssistantConfig, MqttConfig, NodeConfig
|
|
@@ -30,9 +31,9 @@ def test_publish(mock_mqtt_client: Mock, protocol: str) -> None:
|
|
|
30
31
|
|
|
31
32
|
@pytest.mark.asyncio
|
|
32
33
|
async def test_handler(mock_mqtt_client: Mock) -> None:
|
|
33
|
-
config = MqttConfig
|
|
34
|
-
hass_config = HomeAssistantConfig
|
|
35
|
-
node_config = NodeConfig
|
|
34
|
+
config = OmegaConf.structured(MqttConfig)
|
|
35
|
+
hass_config = OmegaConf.structured(HomeAssistantConfig)
|
|
36
|
+
node_config = OmegaConf.structured(NodeConfig)
|
|
36
37
|
node_config.name = "testing"
|
|
37
38
|
with patch("updates2mqtt.mqtt.mqtt.Client", new=mock_mqtt_client):
|
|
38
39
|
uut = MqttPublisher(config, node_config, hass_config)
|
|
@@ -40,7 +41,7 @@ async def test_handler(mock_mqtt_client: Mock) -> None:
|
|
|
40
41
|
|
|
41
42
|
provider = Mock(spec=ReleaseProvider)
|
|
42
43
|
provider.source_type = "unit_test"
|
|
43
|
-
discovery = Discovery(provider, "qux",
|
|
44
|
+
discovery = Discovery(provider, "qux", "test-mqtt-123", "node004")
|
|
44
45
|
provider.command.return_value = discovery
|
|
45
46
|
provider.hass_state_format.return_value = {}
|
|
46
47
|
|
|
@@ -58,9 +59,10 @@ async def test_handler(mock_mqtt_client: Mock) -> None:
|
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
async def test_execute_command_remote(mock_mqtt_client: Mock, mock_provider: ReleaseProvider) -> None:
|
|
61
|
-
config = MqttConfig
|
|
62
|
-
hass_config = HomeAssistantConfig
|
|
63
|
-
node_config = NodeConfig
|
|
62
|
+
config = OmegaConf.structured(MqttConfig)
|
|
63
|
+
hass_config = OmegaConf.structured(HomeAssistantConfig)
|
|
64
|
+
node_config = OmegaConf.structured(NodeConfig)
|
|
65
|
+
node_config.name = "TESTBED"
|
|
64
66
|
|
|
65
67
|
with patch.object(paho.mqtt.client.Client, "__new__", lambda *_args, **_kwargs: mock_mqtt_client):
|
|
66
68
|
uut = MqttPublisher(config, node_config, hass_config)
|
|
@@ -79,7 +81,7 @@ async def test_execute_command_remote(mock_mqtt_client: Mock, mock_provider: Rel
|
|
|
79
81
|
{
|
|
80
82
|
"installed_version": "v2",
|
|
81
83
|
"latest_version": "v2",
|
|
82
|
-
"title": "Update for fooey on
|
|
84
|
+
"title": "Update for fooey on node002",
|
|
83
85
|
"in_progress": False,
|
|
84
86
|
}
|
|
85
87
|
),
|
|
@@ -90,9 +92,10 @@ async def test_execute_command_remote(mock_mqtt_client: Mock, mock_provider: Rel
|
|
|
90
92
|
|
|
91
93
|
@pytest.mark.asyncio
|
|
92
94
|
async def test_execute_command_local(mock_mqtt_client: Mock, mock_provider: ReleaseProvider) -> None:
|
|
93
|
-
config = MqttConfig
|
|
94
|
-
hass_config = HomeAssistantConfig
|
|
95
|
-
node_config = NodeConfig
|
|
95
|
+
config = OmegaConf.structured(MqttConfig)
|
|
96
|
+
hass_config = OmegaConf.structured(HomeAssistantConfig)
|
|
97
|
+
node_config = OmegaConf.structured(NodeConfig)
|
|
98
|
+
node_config.name = "TESTBED"
|
|
96
99
|
|
|
97
100
|
with patch.object(paho.mqtt.client.Client, "__new__", lambda *_args, **_kwargs: mock_mqtt_client):
|
|
98
101
|
uut = MqttPublisher(config, node_config, hass_config)
|
|
@@ -101,7 +104,8 @@ async def test_execute_command_local(mock_mqtt_client: Mock, mock_provider: Rele
|
|
|
101
104
|
|
|
102
105
|
uut.subscribe_hass_command(mock_provider)
|
|
103
106
|
|
|
104
|
-
discovery =
|
|
107
|
+
discovery = mock_provider.resolve("test")
|
|
108
|
+
assert discovery is not None
|
|
105
109
|
uut.local_message(discovery, "install")
|
|
106
110
|
await asyncio.sleep(1)
|
|
107
111
|
|
|
@@ -111,7 +115,7 @@ async def test_execute_command_local(mock_mqtt_client: Mock, mock_provider: Rele
|
|
|
111
115
|
{
|
|
112
116
|
"installed_version": "v2",
|
|
113
117
|
"latest_version": "v2",
|
|
114
|
-
"title": "Update for fooey on
|
|
118
|
+
"title": "Update for fooey on node002",
|
|
115
119
|
"in_progress": False,
|
|
116
120
|
}
|
|
117
121
|
),
|
|
@@ -122,9 +126,9 @@ async def test_execute_command_local(mock_mqtt_client: Mock, mock_provider: Rele
|
|
|
122
126
|
|
|
123
127
|
@pytest.mark.asyncio
|
|
124
128
|
async def test_stop(mock_mqtt_client: Mock, mock_provider: ReleaseProvider) -> None:
|
|
125
|
-
config = MqttConfig
|
|
126
|
-
hass_config = HomeAssistantConfig
|
|
127
|
-
node_config = NodeConfig
|
|
129
|
+
config = OmegaConf.structured(MqttConfig)
|
|
130
|
+
hass_config = OmegaConf.structured(HomeAssistantConfig)
|
|
131
|
+
node_config = OmegaConf.structured(NodeConfig)
|
|
128
132
|
|
|
129
133
|
with patch.object(paho.mqtt.client.Client, "__new__", lambda *_args, **_kwargs: mock_mqtt_client):
|
|
130
134
|
uut = MqttPublisher(config, node_config, hass_config)
|
|
@@ -150,63 +150,63 @@ wheels = [
|
|
|
150
150
|
|
|
151
151
|
[[package]]
|
|
152
152
|
name = "coverage"
|
|
153
|
-
version = "7.
|
|
154
|
-
source = { registry = "https://pypi.org/simple" }
|
|
155
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
156
|
-
wheels = [
|
|
157
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
158
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
159
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
160
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
161
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
162
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
163
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
164
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
165
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
166
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
167
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
168
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
169
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
170
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
171
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
172
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
173
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
174
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
175
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
176
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
177
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
178
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
179
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
180
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
181
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
182
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
183
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
184
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
185
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
186
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
187
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
188
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
189
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
190
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
191
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
192
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
193
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
194
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
195
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
196
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
197
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
198
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
199
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
200
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
201
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
202
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
203
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
204
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
205
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
206
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
207
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
208
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
209
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
153
|
+
version = "7.13.0"
|
|
154
|
+
source = { registry = "https://pypi.org/simple" }
|
|
155
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b6/45/2c665ca77ec32ad67e25c77daf1cee28ee4558f3bc571cdbaf88a00b9f23/coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936", size = 820905, upload-time = "2025-12-08T13:14:38.055Z" }
|
|
156
|
+
wheels = [
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/7c/cc/bce226595eb3bf7d13ccffe154c3c487a22222d87ff018525ab4dd2e9542/coverage-7.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:28ee1c96109974af104028a8ef57cec21447d42d0e937c0275329272e370ebcf", size = 218297, upload-time = "2025-12-08T13:13:10.977Z" },
|
|
158
|
+
{ url = "https://files.pythonhosted.org/packages/3b/9f/73c4d34600aae03447dff3d7ad1d0ac649856bfb87d1ca7d681cfc913f9e/coverage-7.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e97353dcc5587b85986cda4ff3ec98081d7e84dd95e8b2a6d59820f0545f8a", size = 218673, upload-time = "2025-12-08T13:13:12.562Z" },
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/63/ab/8fa097db361a1e8586535ae5073559e6229596b3489ec3ef2f5b38df8cb2/coverage-7.13.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:99acd4dfdfeb58e1937629eb1ab6ab0899b131f183ee5f23e0b5da5cba2fec74", size = 249652, upload-time = "2025-12-08T13:13:13.909Z" },
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/90/3a/9bfd4de2ff191feb37ef9465855ca56a6f2f30a3bca172e474130731ac3d/coverage-7.13.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ff45e0cd8451e293b63ced93161e189780baf444119391b3e7d25315060368a6", size = 252251, upload-time = "2025-12-08T13:13:15.553Z" },
|
|
161
|
+
{ url = "https://files.pythonhosted.org/packages/df/61/b5d8105f016e1b5874af0d7c67542da780ccd4a5f2244a433d3e20ceb1ad/coverage-7.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f4f72a85316d8e13234cafe0a9f81b40418ad7a082792fa4165bd7d45d96066b", size = 253492, upload-time = "2025-12-08T13:13:16.849Z" },
|
|
162
|
+
{ url = "https://files.pythonhosted.org/packages/f3/b8/0fad449981803cc47a4694768b99823fb23632150743f9c83af329bb6090/coverage-7.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11c21557d0e0a5a38632cbbaca5f008723b26a89d70db6315523df6df77d6232", size = 249850, upload-time = "2025-12-08T13:13:18.142Z" },
|
|
163
|
+
{ url = "https://files.pythonhosted.org/packages/9a/e9/8d68337c3125014d918cf4327d5257553a710a2995a6a6de2ac77e5aa429/coverage-7.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76541dc8d53715fb4f7a3a06b34b0dc6846e3c69bc6204c55653a85dd6220971", size = 251633, upload-time = "2025-12-08T13:13:19.56Z" },
|
|
164
|
+
{ url = "https://files.pythonhosted.org/packages/55/14/d4112ab26b3a1bc4b3c1295d8452dcf399ed25be4cf649002fb3e64b2d93/coverage-7.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6e9e451dee940a86789134b6b0ffbe31c454ade3b849bb8a9d2cca2541a8e91d", size = 249586, upload-time = "2025-12-08T13:13:20.883Z" },
|
|
165
|
+
{ url = "https://files.pythonhosted.org/packages/2c/a9/22b0000186db663b0d82f86c2f1028099ae9ac202491685051e2a11a5218/coverage-7.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5c67dace46f361125e6b9cace8fe0b729ed8479f47e70c89b838d319375c8137", size = 249412, upload-time = "2025-12-08T13:13:22.22Z" },
|
|
166
|
+
{ url = "https://files.pythonhosted.org/packages/a1/2e/42d8e0d9e7527fba439acdc6ed24a2b97613b1dc85849b1dd935c2cffef0/coverage-7.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f59883c643cb19630500f57016f76cfdcd6845ca8c5b5ea1f6e17f74c8e5f511", size = 251191, upload-time = "2025-12-08T13:13:23.899Z" },
|
|
167
|
+
{ url = "https://files.pythonhosted.org/packages/a4/af/8c7af92b1377fd8860536aadd58745119252aaaa71a5213e5a8e8007a9f5/coverage-7.13.0-cp313-cp313-win32.whl", hash = "sha256:58632b187be6f0be500f553be41e277712baa278147ecb7559983c6d9faf7ae1", size = 220829, upload-time = "2025-12-08T13:13:25.182Z" },
|
|
168
|
+
{ url = "https://files.pythonhosted.org/packages/58/f9/725e8bf16f343d33cbe076c75dc8370262e194ff10072c0608b8e5cf33a3/coverage-7.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:73419b89f812f498aca53f757dd834919b48ce4799f9d5cad33ca0ae442bdb1a", size = 221640, upload-time = "2025-12-08T13:13:26.836Z" },
|
|
169
|
+
{ url = "https://files.pythonhosted.org/packages/8a/ff/e98311000aa6933cc79274e2b6b94a2fe0fe3434fca778eba82003675496/coverage-7.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:eb76670874fdd6091eedcc856128ee48c41a9bbbb9c3f1c7c3cf169290e3ffd6", size = 220269, upload-time = "2025-12-08T13:13:28.116Z" },
|
|
170
|
+
{ url = "https://files.pythonhosted.org/packages/cf/cf/bbaa2e1275b300343ea865f7d424cc0a2e2a1df6925a070b2b2d5d765330/coverage-7.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6e63ccc6e0ad8986386461c3c4b737540f20426e7ec932f42e030320896c311a", size = 218990, upload-time = "2025-12-08T13:13:29.463Z" },
|
|
171
|
+
{ url = "https://files.pythonhosted.org/packages/21/1d/82f0b3323b3d149d7672e7744c116e9c170f4957e0c42572f0366dbb4477/coverage-7.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:494f5459ffa1bd45e18558cd98710c36c0b8fbfa82a5eabcbe671d80ecffbfe8", size = 219340, upload-time = "2025-12-08T13:13:31.524Z" },
|
|
172
|
+
{ url = "https://files.pythonhosted.org/packages/fb/e3/fe3fd4702a3832a255f4d43013eacb0ef5fc155a5960ea9269d8696db28b/coverage-7.13.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:06cac81bf10f74034e055e903f5f946e3e26fc51c09fc9f584e4a1605d977053", size = 260638, upload-time = "2025-12-08T13:13:32.965Z" },
|
|
173
|
+
{ url = "https://files.pythonhosted.org/packages/ad/01/63186cb000307f2b4da463f72af9b85d380236965574c78e7e27680a2593/coverage-7.13.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f2ffc92b46ed6e6760f1d47a71e56b5664781bc68986dbd1836b2b70c0ce2071", size = 262705, upload-time = "2025-12-08T13:13:34.378Z" },
|
|
174
|
+
{ url = "https://files.pythonhosted.org/packages/7c/a1/c0dacef0cc865f2455d59eed3548573ce47ed603205ffd0735d1d78b5906/coverage-7.13.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0602f701057c6823e5db1b74530ce85f17c3c5be5c85fc042ac939cbd909426e", size = 265125, upload-time = "2025-12-08T13:13:35.73Z" },
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/ef/92/82b99223628b61300bd382c205795533bed021505eab6dd86e11fb5d7925/coverage-7.13.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:25dc33618d45456ccb1d37bce44bc78cf269909aa14c4db2e03d63146a8a1493", size = 259844, upload-time = "2025-12-08T13:13:37.69Z" },
|
|
176
|
+
{ url = "https://files.pythonhosted.org/packages/cf/2c/89b0291ae4e6cd59ef042708e1c438e2290f8c31959a20055d8768349ee2/coverage-7.13.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:71936a8b3b977ddd0b694c28c6a34f4fff2e9dd201969a4ff5d5fc7742d614b0", size = 262700, upload-time = "2025-12-08T13:13:39.525Z" },
|
|
177
|
+
{ url = "https://files.pythonhosted.org/packages/bf/f9/a5f992efae1996245e796bae34ceb942b05db275e4b34222a9a40b9fbd3b/coverage-7.13.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:936bc20503ce24770c71938d1369461f0c5320830800933bc3956e2a4ded930e", size = 260321, upload-time = "2025-12-08T13:13:41.172Z" },
|
|
178
|
+
{ url = "https://files.pythonhosted.org/packages/4c/89/a29f5d98c64fedbe32e2ac3c227fbf78edc01cc7572eee17d61024d89889/coverage-7.13.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:af0a583efaacc52ae2521f8d7910aff65cdb093091d76291ac5820d5e947fc1c", size = 259222, upload-time = "2025-12-08T13:13:43.282Z" },
|
|
179
|
+
{ url = "https://files.pythonhosted.org/packages/b3/c3/940fe447aae302a6701ee51e53af7e08b86ff6eed7631e5740c157ee22b9/coverage-7.13.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f1c23e24a7000da892a312fb17e33c5f94f8b001de44b7cf8ba2e36fbd15859e", size = 261411, upload-time = "2025-12-08T13:13:44.72Z" },
|
|
180
|
+
{ url = "https://files.pythonhosted.org/packages/eb/31/12a4aec689cb942a89129587860ed4d0fd522d5fda81237147fde554b8ae/coverage-7.13.0-cp313-cp313t-win32.whl", hash = "sha256:5f8a0297355e652001015e93be345ee54393e45dc3050af4a0475c5a2b767d46", size = 221505, upload-time = "2025-12-08T13:13:46.332Z" },
|
|
181
|
+
{ url = "https://files.pythonhosted.org/packages/65/8c/3b5fe3259d863572d2b0827642c50c3855d26b3aefe80bdc9eba1f0af3b0/coverage-7.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6abb3a4c52f05e08460bd9acf04fec027f8718ecaa0d09c40ffbc3fbd70ecc39", size = 222569, upload-time = "2025-12-08T13:13:47.79Z" },
|
|
182
|
+
{ url = "https://files.pythonhosted.org/packages/b0/39/f71fa8316a96ac72fc3908839df651e8eccee650001a17f2c78cdb355624/coverage-7.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:3ad968d1e3aa6ce5be295ab5fe3ae1bf5bb4769d0f98a80a0252d543a2ef2e9e", size = 220841, upload-time = "2025-12-08T13:13:49.243Z" },
|
|
183
|
+
{ url = "https://files.pythonhosted.org/packages/f8/4b/9b54bedda55421449811dcd5263a2798a63f48896c24dfb92b0f1b0845bd/coverage-7.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:453b7ec753cf5e4356e14fe858064e5520c460d3bbbcb9c35e55c0d21155c256", size = 218343, upload-time = "2025-12-08T13:13:50.811Z" },
|
|
184
|
+
{ url = "https://files.pythonhosted.org/packages/59/df/c3a1f34d4bba2e592c8979f924da4d3d4598b0df2392fbddb7761258e3dc/coverage-7.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:af827b7cbb303e1befa6c4f94fd2bf72f108089cfa0f8abab8f4ca553cf5ca5a", size = 218672, upload-time = "2025-12-08T13:13:52.284Z" },
|
|
185
|
+
{ url = "https://files.pythonhosted.org/packages/07/62/eec0659e47857698645ff4e6ad02e30186eb8afd65214fd43f02a76537cb/coverage-7.13.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9987a9e4f8197a1000280f7cc089e3ea2c8b3c0a64d750537809879a7b4ceaf9", size = 249715, upload-time = "2025-12-08T13:13:53.791Z" },
|
|
186
|
+
{ url = "https://files.pythonhosted.org/packages/23/2d/3c7ff8b2e0e634c1f58d095f071f52ed3c23ff25be524b0ccae8b71f99f8/coverage-7.13.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3188936845cd0cb114fa6a51842a304cdbac2958145d03be2377ec41eb285d19", size = 252225, upload-time = "2025-12-08T13:13:55.274Z" },
|
|
187
|
+
{ url = "https://files.pythonhosted.org/packages/aa/ac/fb03b469d20e9c9a81093575003f959cf91a4a517b783aab090e4538764b/coverage-7.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2bdb3babb74079f021696cb46b8bb5f5661165c385d3a238712b031a12355be", size = 253559, upload-time = "2025-12-08T13:13:57.161Z" },
|
|
188
|
+
{ url = "https://files.pythonhosted.org/packages/29/62/14afa9e792383c66cc0a3b872a06ded6e4ed1079c7d35de274f11d27064e/coverage-7.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7464663eaca6adba4175f6c19354feea61ebbdd735563a03d1e472c7072d27bb", size = 249724, upload-time = "2025-12-08T13:13:58.692Z" },
|
|
189
|
+
{ url = "https://files.pythonhosted.org/packages/31/b7/333f3dab2939070613696ab3ee91738950f0467778c6e5a5052e840646b7/coverage-7.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8069e831f205d2ff1f3d355e82f511eb7c5522d7d413f5db5756b772ec8697f8", size = 251582, upload-time = "2025-12-08T13:14:00.642Z" },
|
|
190
|
+
{ url = "https://files.pythonhosted.org/packages/81/cb/69162bda9381f39b2287265d7e29ee770f7c27c19f470164350a38318764/coverage-7.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6fb2d5d272341565f08e962cce14cdf843a08ac43bd621783527adb06b089c4b", size = 249538, upload-time = "2025-12-08T13:14:02.556Z" },
|
|
191
|
+
{ url = "https://files.pythonhosted.org/packages/e0/76/350387b56a30f4970abe32b90b2a434f87d29f8b7d4ae40d2e8a85aacfb3/coverage-7.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5e70f92ef89bac1ac8a99b3324923b4749f008fdbd7aa9cb35e01d7a284a04f9", size = 249349, upload-time = "2025-12-08T13:14:04.015Z" },
|
|
192
|
+
{ url = "https://files.pythonhosted.org/packages/86/0d/7f6c42b8d59f4c7e43ea3059f573c0dcfed98ba46eb43c68c69e52ae095c/coverage-7.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4b5de7d4583e60d5fd246dd57fcd3a8aa23c6e118a8c72b38adf666ba8e7e927", size = 251011, upload-time = "2025-12-08T13:14:05.505Z" },
|
|
193
|
+
{ url = "https://files.pythonhosted.org/packages/d7/f1/4bb2dff379721bb0b5c649d5c5eaf438462cad824acf32eb1b7ca0c7078e/coverage-7.13.0-cp314-cp314-win32.whl", hash = "sha256:a6c6e16b663be828a8f0b6c5027d36471d4a9f90d28444aa4ced4d48d7d6ae8f", size = 221091, upload-time = "2025-12-08T13:14:07.127Z" },
|
|
194
|
+
{ url = "https://files.pythonhosted.org/packages/ba/44/c239da52f373ce379c194b0ee3bcc121020e397242b85f99e0afc8615066/coverage-7.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:0900872f2fdb3ee5646b557918d02279dc3af3dfb39029ac4e945458b13f73bc", size = 221904, upload-time = "2025-12-08T13:14:08.542Z" },
|
|
195
|
+
{ url = "https://files.pythonhosted.org/packages/89/1f/b9f04016d2a29c2e4a0307baefefad1a4ec5724946a2b3e482690486cade/coverage-7.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:3a10260e6a152e5f03f26db4a407c4c62d3830b9af9b7c0450b183615f05d43b", size = 220480, upload-time = "2025-12-08T13:14:10.958Z" },
|
|
196
|
+
{ url = "https://files.pythonhosted.org/packages/16/d4/364a1439766c8e8647860584171c36010ca3226e6e45b1753b1b249c5161/coverage-7.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9097818b6cc1cfb5f174e3263eba4a62a17683bcfe5c4b5d07f4c97fa51fbf28", size = 219074, upload-time = "2025-12-08T13:14:13.345Z" },
|
|
197
|
+
{ url = "https://files.pythonhosted.org/packages/ce/f4/71ba8be63351e099911051b2089662c03d5671437a0ec2171823c8e03bec/coverage-7.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0018f73dfb4301a89292c73be6ba5f58722ff79f51593352759c1790ded1cabe", size = 219342, upload-time = "2025-12-08T13:14:15.02Z" },
|
|
198
|
+
{ url = "https://files.pythonhosted.org/packages/5e/25/127d8ed03d7711a387d96f132589057213e3aef7475afdaa303412463f22/coverage-7.13.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:166ad2a22ee770f5656e1257703139d3533b4a0b6909af67c6b4a3adc1c98657", size = 260713, upload-time = "2025-12-08T13:14:16.907Z" },
|
|
199
|
+
{ url = "https://files.pythonhosted.org/packages/fd/db/559fbb6def07d25b2243663b46ba9eb5a3c6586c0c6f4e62980a68f0ee1c/coverage-7.13.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f6aaef16d65d1787280943f1c8718dc32e9cf141014e4634d64446702d26e0ff", size = 262825, upload-time = "2025-12-08T13:14:18.68Z" },
|
|
200
|
+
{ url = "https://files.pythonhosted.org/packages/37/99/6ee5bf7eff884766edb43bd8736b5e1c5144d0fe47498c3779326fe75a35/coverage-7.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e999e2dcc094002d6e2c7bbc1fb85b58ba4f465a760a8014d97619330cdbbbf3", size = 265233, upload-time = "2025-12-08T13:14:20.55Z" },
|
|
201
|
+
{ url = "https://files.pythonhosted.org/packages/d8/90/92f18fe0356ea69e1f98f688ed80cec39f44e9f09a1f26a1bbf017cc67f2/coverage-7.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:00c3d22cf6fb1cf3bf662aaaa4e563be8243a5ed2630339069799835a9cc7f9b", size = 259779, upload-time = "2025-12-08T13:14:22.367Z" },
|
|
202
|
+
{ url = "https://files.pythonhosted.org/packages/90/5d/b312a8b45b37a42ea7d27d7d3ff98ade3a6c892dd48d1d503e773503373f/coverage-7.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22ccfe8d9bb0d6134892cbe1262493a8c70d736b9df930f3f3afae0fe3ac924d", size = 262700, upload-time = "2025-12-08T13:14:24.309Z" },
|
|
203
|
+
{ url = "https://files.pythonhosted.org/packages/63/f8/b1d0de5c39351eb71c366f872376d09386640840a2e09b0d03973d791e20/coverage-7.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9372dff5ea15930fea0445eaf37bbbafbc771a49e70c0aeed8b4e2c2614cc00e", size = 260302, upload-time = "2025-12-08T13:14:26.068Z" },
|
|
204
|
+
{ url = "https://files.pythonhosted.org/packages/aa/7c/d42f4435bc40c55558b3109a39e2d456cddcec37434f62a1f1230991667a/coverage-7.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:69ac2c492918c2461bc6ace42d0479638e60719f2a4ef3f0815fa2df88e9f940", size = 259136, upload-time = "2025-12-08T13:14:27.604Z" },
|
|
205
|
+
{ url = "https://files.pythonhosted.org/packages/b8/d3/23413241dc04d47cfe19b9a65b32a2edd67ecd0b817400c2843ebc58c847/coverage-7.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:739c6c051a7540608d097b8e13c76cfa85263ced467168dc6b477bae3df7d0e2", size = 261467, upload-time = "2025-12-08T13:14:29.09Z" },
|
|
206
|
+
{ url = "https://files.pythonhosted.org/packages/13/e6/6e063174500eee216b96272c0d1847bf215926786f85c2bd024cf4d02d2f/coverage-7.13.0-cp314-cp314t-win32.whl", hash = "sha256:fe81055d8c6c9de76d60c94ddea73c290b416e061d40d542b24a5871bad498b7", size = 221875, upload-time = "2025-12-08T13:14:31.106Z" },
|
|
207
|
+
{ url = "https://files.pythonhosted.org/packages/3b/46/f4fb293e4cbe3620e3ac2a3e8fd566ed33affb5861a9b20e3dd6c1896cbc/coverage-7.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:445badb539005283825959ac9fa4a28f712c214b65af3a2c464f1adc90f5fcbc", size = 222982, upload-time = "2025-12-08T13:14:33.1Z" },
|
|
208
|
+
{ url = "https://files.pythonhosted.org/packages/68/62/5b3b9018215ed9733fbd1ae3b2ed75c5de62c3b55377a52cae732e1b7805/coverage-7.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:de7f6748b890708578fc4b7bb967d810aeb6fcc9bff4bb77dbca77dab2f9df6a", size = 221016, upload-time = "2025-12-08T13:14:34.601Z" },
|
|
209
|
+
{ url = "https://files.pythonhosted.org/packages/8d/4c/1968f32fb9a2604645827e11ff84a31e59d532e01995f904723b4f5328b3/coverage-7.13.0-py3-none-any.whl", hash = "sha256:850d2998f380b1e266459ca5b47bc9e7daf9af1d070f66317972f382d46f1904", size = 210068, upload-time = "2025-12-08T13:14:36.236Z" },
|
|
210
210
|
]
|
|
211
211
|
|
|
212
212
|
[[package]]
|
|
@@ -996,15 +996,15 @@ wheels = [
|
|
|
996
996
|
|
|
997
997
|
[[package]]
|
|
998
998
|
name = "pymdown-extensions"
|
|
999
|
-
version = "10.
|
|
999
|
+
version = "10.18"
|
|
1000
1000
|
source = { registry = "https://pypi.org/simple" }
|
|
1001
1001
|
dependencies = [
|
|
1002
1002
|
{ name = "markdown" },
|
|
1003
1003
|
{ name = "pyyaml" },
|
|
1004
1004
|
]
|
|
1005
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1005
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d4/95/e4fa281e3f13b3d9c4aaebb21ef44879840325fa418276dd921209a5e9f9/pymdown_extensions-10.18.tar.gz", hash = "sha256:20252abe6367354b24191431617a072ee6be9f68c5afcc74ea5573508a61f9e5", size = 847697, upload-time = "2025-12-07T17:22:12.857Z" }
|
|
1006
1006
|
wheels = [
|
|
1007
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1007
|
+
{ url = "https://files.pythonhosted.org/packages/46/a4/aa2bada4a2fd648f40f19affa55d2c01dc7ff5ea9cffd3dfdeb6114951db/pymdown_extensions-10.18-py3-none-any.whl", hash = "sha256:090bca72be43f7d3186374e23c782899dbef9dc153ef24c59dcd3c346f9ffcae", size = 266703, upload-time = "2025-12-07T17:22:11.22Z" },
|
|
1008
1008
|
]
|
|
1009
1009
|
|
|
1010
1010
|
[[package]]
|
|
@@ -1314,7 +1314,7 @@ wheels = [
|
|
|
1314
1314
|
|
|
1315
1315
|
[[package]]
|
|
1316
1316
|
name = "updates2mqtt"
|
|
1317
|
-
version = "1.5.
|
|
1317
|
+
version = "1.5.1"
|
|
1318
1318
|
source = { editable = "." }
|
|
1319
1319
|
dependencies = [
|
|
1320
1320
|
{ name = "docker" },
|
|
@@ -1399,11 +1399,11 @@ mkdocs = [
|
|
|
1399
1399
|
|
|
1400
1400
|
[[package]]
|
|
1401
1401
|
name = "urllib3"
|
|
1402
|
-
version = "2.6.
|
|
1402
|
+
version = "2.6.1"
|
|
1403
1403
|
source = { registry = "https://pypi.org/simple" }
|
|
1404
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1404
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5e/1d/0f3a93cca1ac5e8287842ed4eebbd0f7a991315089b1a0b01c7788aa7b63/urllib3-2.6.1.tar.gz", hash = "sha256:5379eb6e1aba4088bae84f8242960017ec8d8e3decf30480b3a1abdaa9671a3f", size = 432678, upload-time = "2025-12-08T15:25:26.773Z" }
|
|
1405
1405
|
wheels = [
|
|
1406
|
-
{ url = "https://files.pythonhosted.org/packages/56/
|
|
1406
|
+
{ url = "https://files.pythonhosted.org/packages/bc/56/190ceb8cb10511b730b564fb1e0293fa468363dbad26145c34928a60cb0c/urllib3-2.6.1-py3-none-any.whl", hash = "sha256:e67d06fe947c36a7ca39f4994b08d73922d40e6cca949907be05efa6fd75110b", size = 131138, upload-time = "2025-12-08T15:25:25.51Z" },
|
|
1407
1407
|
]
|
|
1408
1408
|
|
|
1409
1409
|
[[package]]
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
log:
|
|
2
|
-
level: INFO
|
|
3
|
-
node:
|
|
4
|
-
name: Jeys-MacBook-Pro
|
|
5
|
-
git_path: /usr/bin/git
|
|
6
|
-
healthcheck:
|
|
7
|
-
enabled: true
|
|
8
|
-
interval: 300
|
|
9
|
-
topic_template: healthcheck/{node_name}/updates2mqtt
|
|
10
|
-
mqtt:
|
|
11
|
-
host: ${oc.env:MQTT_HOST,localhost}
|
|
12
|
-
user: ${oc.env:MQTT_USER,null}
|
|
13
|
-
password: ${oc.env:MQTT_PASS,null}
|
|
14
|
-
port: ${oc.decode:${oc.env:MQTT_PORT,1883}}
|
|
15
|
-
topic_root: updates2mqtt
|
|
16
|
-
homeassistant:
|
|
17
|
-
discovery:
|
|
18
|
-
prefix: homeassistant
|
|
19
|
-
enabled: true
|
|
20
|
-
state_topic_suffix: state
|
|
21
|
-
docker:
|
|
22
|
-
enabled: true
|
|
23
|
-
allow_pull: true
|
|
24
|
-
allow_restart: true
|
|
25
|
-
allow_build: true
|
|
26
|
-
compose_version: v2
|
|
27
|
-
default_entity_picture_url: https://www.docker.com/wp-content/uploads/2022/03/Moby-logo.png
|
|
28
|
-
device_icon: mdi:docker
|
|
29
|
-
discover_metadata:
|
|
30
|
-
linuxserver.io:
|
|
31
|
-
enabled: true
|
|
32
|
-
cache_ttl: 604800
|
|
33
|
-
scan_interval: 10800
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|