diff --git a/API.md b/API.md index 334e2a62e..8c7a4d40a 100644 --- a/API.md +++ b/API.md @@ -350,6 +350,10 @@ Load host configs from a USB stick. } ``` +- POST `/hardware/trigger` + +Trigger an udev reload + ### Home Assistant - GET `/homeassistant/info` diff --git a/hassio/addons/addon.py b/hassio/addons/addon.py index 375ad52a7..be4fc2238 100644 --- a/hassio/addons/addon.py +++ b/hassio/addons/addon.py @@ -471,6 +471,7 @@ class Addon(AddonModel): if self.with_audio: self.write_asound() + # Start Add-on try: await self.instance.run() except DockerAPIError: diff --git a/hassio/addons/model.py b/hassio/addons/model.py index 5e4bb20a3..1a895de61 100644 --- a/hassio/addons/model.py +++ b/hassio/addons/model.py @@ -1,8 +1,8 @@ """Init file for Hass.io add-ons.""" -from distutils.version import StrictVersion from pathlib import Path from typing import Any, Awaitable, Dict, List, Optional +from packaging import version as pkg_version import voluptuous as vol from ..const import ( @@ -482,7 +482,9 @@ class AddonModel(CoreSysAttributes): # Home Assistant version = config.get(ATTR_HOMEASSISTANT) or self.sys_homeassistant.version - if StrictVersion(self.sys_homeassistant.version) < StrictVersion(version): + if pkg_version.parse(self.sys_homeassistant.version) < pkg_version.parse( + version + ): return False return True diff --git a/hassio/api/__init__.py b/hassio/api/__init__.py index 628d7795d..bdde0260f 100644 --- a/hassio/api/__init__.py +++ b/hassio/api/__init__.py @@ -101,6 +101,7 @@ class RestAPI(CoreSysAttributes): [ web.get("/hardware/info", api_hardware.info), web.get("/hardware/audio", api_hardware.audio), + web.post("/hardware/trigger", api_hardware.trigger), ] ) diff --git a/hassio/api/hardware.py b/hassio/api/hardware.py index f9fa125a3..1d71b67f7 100644 --- a/hassio/api/hardware.py +++ b/hassio/api/hardware.py @@ -1,5 +1,9 @@ """Init file for Hass.io hardware RESTful API.""" +import asyncio import logging +from typing import Any, Dict + +from aiohttp import web from .utils import api_process from ..const import ( @@ -19,7 +23,7 @@ class APIHardware(CoreSysAttributes): """Handle RESTful API for hardware functions.""" @api_process - async def info(self, request): + async def info(self, request: web.Request) -> Dict[str, Any]: """Show hardware info.""" return { ATTR_SERIAL: list( @@ -32,7 +36,7 @@ class APIHardware(CoreSysAttributes): } @api_process - async def audio(self, request): + async def audio(self, request: web.Request) -> Dict[str, Any]: """Show ALSA audio devices.""" return { ATTR_AUDIO: { @@ -40,3 +44,8 @@ class APIHardware(CoreSysAttributes): ATTR_OUTPUT: self.sys_host.alsa.output_devices, } } + + @api_process + def trigger(self, request: web.Request) -> None: + """Trigger a udev device reload.""" + return asyncio.shield(self.sys_hardware.udev_trigger()) diff --git a/hassio/api/security.py b/hassio/api/security.py index bdc0b3bc3..59fd70996 100644 --- a/hassio/api/security.py +++ b/hassio/api/security.py @@ -41,6 +41,7 @@ ADDONS_API_BYPASS = re.compile( r"^(?:" r"|/addons/self/(?!security|update)[^/]+" r"|/info" + r"|/hardware/trigger" r"|/services.*" r"|/discovery.*" r"|/auth" diff --git a/hassio/const.py b/hassio/const.py index fd73e1966..f581d6a24 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -2,7 +2,7 @@ from pathlib import Path from ipaddress import ip_network -HASSIO_VERSION = "184" +HASSIO_VERSION = "185" URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons" diff --git a/hassio/exceptions.py b/hassio/exceptions.py index b03a0c0a3..289433a5f 100644 --- a/hassio/exceptions.py +++ b/hassio/exceptions.py @@ -188,3 +188,10 @@ class JsonFileError(HassioError): class DockerAPIError(HassioError): """Docker API error.""" + + +# Hardware + + +class HardwareNotSupportedError(HassioNotSupportedError): + """Raise if hardware function is not supported.""" diff --git a/hassio/homeassistant.py b/hassio/homeassistant.py index 29966e644..d9fd3c7ff 100644 --- a/hassio/homeassistant.py +++ b/hassio/homeassistant.py @@ -2,7 +2,6 @@ import asyncio from contextlib import asynccontextmanager, suppress from datetime import datetime, timedelta -from distutils.version import StrictVersion from ipaddress import IPv4Address import logging import os @@ -16,6 +15,7 @@ from uuid import UUID import aiohttp from aiohttp import hdrs import attr +from packaging import version as pkg_version from .const import ( ATTR_ACCESS_TOKEN, @@ -80,7 +80,9 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): try: # Evaluate Version if we lost this information if not self.version: - self.version = await self.instance.get_latest_version(key=StrictVersion) + self.version = await self.instance.get_latest_version( + key=pkg_version.parse + ) await self.instance.attach(tag=self.version) except DockerAPIError: diff --git a/hassio/misc/hardware.py b/hassio/misc/hardware.py index 512106377..9ec283c6e 100644 --- a/hassio/misc/hardware.py +++ b/hassio/misc/hardware.py @@ -1,4 +1,5 @@ """Read hardware info from system.""" +import asyncio from datetime import datetime import logging from pathlib import Path @@ -8,6 +9,7 @@ from typing import Any, Dict, Optional, Set import pyudev from ..const import ATTR_DEVICES, ATTR_NAME, ATTR_TYPE, CHAN_ID, CHAN_TYPE +from ..exceptions import HardwareNotSupportedError _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -148,3 +150,14 @@ class Hardware: return None return datetime.utcfromtimestamp(int(found.group(1))) + + async def udev_trigger(self) -> None: + """Trigger a udev reload.""" + proc = await asyncio.create_subprocess_exec("udevadm", "trigger") + + await proc.wait() + if proc.returncode == 0: + return + + _LOGGER.waring("udevadm device triggering fails!") + raise HardwareNotSupportedError() diff --git a/requirements.txt b/requirements.txt index 71a3bc0f4..ec9f64288 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ cpe==1.2.1 cryptography==2.7 docker==4.0.2 gitpython==3.0.2 +packaging==19.1 pytz==2019.2 pyudev==0.21.0 uvloop==0.12.2