Skip to content

Commit d781a04

Browse files
committed
Dest path variables
1 parent a5829ee commit d781a04

File tree

4 files changed

+95
-52
lines changed

4 files changed

+95
-52
lines changed

.devcontainer.json

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,42 @@
11
{
2-
"name": "patch",
3-
"image": "mcr.microsoft.com/devcontainers/python:3.11-bullseye",
4-
"postCreateCommand": "scripts/setup",
5-
"forwardPorts": [
6-
8123
7-
],
8-
"portsAttributes": {
9-
"8123": {
10-
"label": "Home Assistant",
11-
"onAutoForward": "notify"
2+
"name": "patch",
3+
"image": "mcr.microsoft.com/devcontainers/python:3.11-bullseye",
4+
"postCreateCommand": "scripts/setup",
5+
"forwardPorts": [8123],
6+
"portsAttributes": {
7+
"8123": {
8+
"label": "Home Assistant",
9+
"onAutoForward": "notify"
10+
}
11+
},
12+
"customizations": {
13+
"vscode": {
14+
"extensions": [
15+
"charliermarsh.ruff",
16+
"ms-python.python",
17+
"github.vscode-pull-request-github",
18+
"ryanluker.vscode-coverage-gutters",
19+
"ms-python.vscode-pylance",
20+
"esbenp.prettier-vscode",
21+
"streetsidesoftware.code-spell-checker"
22+
],
23+
"settings": {
24+
"files.eol": "\n",
25+
"editor.tabSize": 4,
26+
"python.pythonPath": "/usr/bin/python3",
27+
"python.analysis.autoSearchPaths": false,
28+
"python.testing.pytestArgs": ["tests"],
29+
"python.testing.unittestEnabled": false,
30+
"python.testing.pytestEnabled": true,
31+
"editor.formatOnPaste": false,
32+
"editor.formatOnSave": true,
33+
"editor.formatOnType": true,
34+
"files.trimTrailingWhitespace": true,
35+
"[python]": {
36+
"editor.defaultFormatter": "charliermarsh.ruff"
1237
}
13-
},
14-
"customizations": {
15-
"vscode": {
16-
"extensions": [
17-
"charliermarsh.ruff",
18-
"ms-python.python",
19-
"github.vscode-pull-request-github",
20-
"ryanluker.vscode-coverage-gutters",
21-
"ms-python.vscode-pylance"
22-
],
23-
"settings": {
24-
"files.eol": "\n",
25-
"editor.tabSize": 4,
26-
"python.pythonPath": "/usr/bin/python3",
27-
"python.analysis.autoSearchPaths": false,
28-
"python.testing.pytestArgs": [
29-
"tests"
30-
],
31-
"python.testing.unittestEnabled": false,
32-
"python.testing.pytestEnabled": true,
33-
"editor.formatOnPaste": false,
34-
"editor.formatOnSave": true,
35-
"editor.formatOnType": true,
36-
"files.trimTrailingWhitespace": true,
37-
"[python]": {
38-
"editor.defaultFormatter": "charliermarsh.ruff"
39-
}
40-
}
41-
}
42-
},
43-
"remoteUser": "vscode"
44-
}
38+
}
39+
}
40+
},
41+
"remoteUser": "vscode"
42+
}

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ patch:
2626
files:
2727
- name: adm_mapping.json
2828
base: /share/fileserver/pysiaalarm/base/data
29-
destination: /usr/local/lib/python3.11/site-packages/pysiaalarm/data
29+
destination: "{site-packages}/pysiaalarm/data"
3030
patch: /share/fileserver/pysiaalarm/patch/data
3131
```
3232

@@ -43,6 +43,11 @@ If a patch was applied (to one file or more) the integration initiates a restart
4343

4444
All files must exist (e.g. `base/name`, etc') inside the Home Assistant Core environment. It’s convenient to mount `base` and `patch` directories as [network shares](https://www.home-assistant.io/common-tasks/os#network-storage).
4545

46+
The directories can use the following variables:
47+
48+
1. `site-packages`: path to the location of Python libraries (e.g. `/usr/local/lib/python3.11/site-packages`).
49+
2. `homeassistant`: path to the `homeassistant` directory, i.e. `/usr/src/homeassistant/homeassistant` (the 2nd `/homeassistant` is not a mistake. There is `homeassistant` directory under the root.)
50+
4651
## Install
4752

4853
HACS is the preferred and easier way to install the component, and can be done by using this My button:
@@ -57,8 +62,8 @@ Home Assistant restart is required once the integration files are copied (either
5762

5863
Home Assistant can run in different configurations. A common one is Home Assistant Operating System, which will be used in the explanation below. In this configuration Home Assistant Core runs as a container. The 2 most relevant directories are:
5964

60-
1. `/usr/src/homeassistant`: this is the place with Home Assistant files built from the [Core repository](https://github.com/home-assistant/core).
61-
2. `/usr/local/lib/python3.11/site-packages`: this is the place where Python libraries are installed. (Note: `python3.11` will be changed when Home Assistant upgrades its Python version.)
65+
1. `/usr/src/homeassistant`: this is the place with Home Assistant files built from the [Core repository](https://github.com/home-assistant/core). The variable `homeassistant` can be used as a prefix in the `destination` parameter and it will be resolved to `/usr/src/homeassistant/homeassistant` (the 2nd `/homeassistant` is not a mistake. There is `homeassistant` directory under the root.)
66+
2. `/usr/local/lib/python3.11/site-packages`: this is the place where Python libraries are installed. (Note: `python3.11` will be changed when Home Assistant upgrades its Python version.) The variable `site-packages` can be used as a prefix in the `destination` parameter and it will be resolved automatically.
6267

6368
It’s possible to explore the environment along with the file system structure and content by:
6469

custom_components/patch/__init__.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import datetime
55
from enum import StrEnum
66
import os
7+
import sys
8+
79
import voluptuous as vol
810

911
import aiofiles
1012
import aiofiles.os
1113

12-
from homeassistant import config as config_utils
14+
import homeassistant
1315
from homeassistant.components.homeassistant import SERVICE_HOMEASSISTANT_RESTART
1416
from homeassistant.const import (
1517
CONF_BASE,
@@ -33,12 +35,23 @@
3335
LOGGER,
3436
)
3537

38+
PATH_VARIABLES = {
39+
"site-packages": next(filter(lambda x: x.endswith("site-packages"), sys.path)),
40+
"homeassistant": os.path.dirname(homeassistant.__file__),
41+
}
42+
43+
44+
def expand_path(path: str) -> str:
45+
"""Expand variables in path string."""
46+
return path.format(**PATH_VARIABLES)
47+
48+
3649
CONFIG_FILE_SCHEMA = vol.Schema(
3750
{
3851
vol.Required(CONF_NAME): cv.string,
39-
vol.Required(CONF_BASE): cv.isdir,
40-
vol.Required(CONF_DESTINATION): cv.isdir,
41-
vol.Required(CONF_PATCH): cv.isdir,
52+
vol.Required(CONF_BASE): vol.All(cv.string, expand_path, cv.isdir),
53+
vol.Required(CONF_DESTINATION): vol.All(cv.string, expand_path, cv.isdir),
54+
vol.Required(CONF_PATCH): vol.All(cv.string, expand_path, cv.isdir),
4255
},
4356
extra=vol.ALLOW_EXTRA,
4457
)
@@ -81,12 +94,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
8194

8295
async def async_reload(_: ServiceCall) -> None:
8396
"""Patch the core files using the new configuration."""
84-
config = config_utils.load_yaml_config_file(
85-
hass.config.path(config_utils.YAML_CONFIG_FILE)
97+
config = homeassistant.config.load_yaml_config_file(
98+
hass.config.path(homeassistant.config.YAML_CONFIG_FILE)
8699
)
87100
if DOMAIN not in config:
88101
raise IntegrationError(
89-
f"'{DOMAIN}' section was not found in {config_utils.YAML_CONFIG_FILE}"
102+
f"'{DOMAIN}' section was not found in {homeassistant.config.YAML_CONFIG_FILE}"
90103
)
91104
await Patch(hass, CONFIG_SCHEMA({DOMAIN: config[DOMAIN]})[DOMAIN]).run()
92105

tests/test_init.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
async_fire_time_changed,
3131
)
3232

33+
from custom_components.patch import expand_path
3334
from custom_components.patch.const import (
3435
CONF_DESTINATION,
3536
CONF_FILES,
@@ -284,3 +285,29 @@ async def test_negative_delay(
284285
DOMAIN,
285286
{DOMAIN: {CONF_DELAY: -1}},
286287
)
288+
289+
290+
def test_expand_path() -> None:
291+
"""Test path with variables."""
292+
for variable in ["site-packages", "homeassistant"]:
293+
assert expand_path(f"{{{variable}}}").endswith(f"{os.path.sep}{variable}")
294+
295+
296+
async def test_expand_path_config(
297+
hass: HomeAssistant, freezer: FrozenDateTimeFactory
298+
) -> None:
299+
"""Test configuration with variables."""
300+
await async_setup(
301+
hass,
302+
{
303+
CONF_FILES: [
304+
{
305+
CONF_NAME: "__init__.py",
306+
CONF_BASE: "{site-packages}/aiofiles",
307+
CONF_DESTINATION: "{site-packages}/aiofiles",
308+
CONF_PATCH: "{site-packages}/aiofiles",
309+
}
310+
]
311+
},
312+
)
313+
await async_next_day(hass, freezer)

0 commit comments

Comments
 (0)