From dd603e1ec22f3223b5807b3739e9821fa2dc5a06 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 6 Feb 2020 10:48:27 +0100 Subject: [PATCH] Support basic video mapping (#1483) * Support basic video mapping * Fix regex --- API.md | 3 ++- hassio/addons/model.py | 6 ++++++ hassio/addons/validate.py | 2 ++ hassio/api/addons.py | 2 ++ hassio/const.py | 1 + hassio/docker/addon.py | 5 +++++ hassio/misc/hardware.py | 42 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 59 insertions(+), 2 deletions(-) diff --git a/API.md b/API.md index 5e24bb36c..d90abace3 100644 --- a/API.md +++ b/API.md @@ -52,7 +52,7 @@ The addons from `addons` are only installed one. "slug": "xy", "description": "description", "repository": "12345678|null", - "version": "LAST_VERSION", + "version": "LATEST_VERSION", "installed": "INSTALL_VERSION", "icon": "bool", "logo": "bool", @@ -537,6 +537,7 @@ Get all available addons. "kernel_modules": "bool", "devicetree": "bool", "docker_api": "bool", + "video": "bool", "audio": "bool", "audio_input": "null|0,0", "audio_output": "null|0,0", diff --git a/hassio/addons/model.py b/hassio/addons/model.py index 1d933936c..debc0ebe8 100644 --- a/hassio/addons/model.py +++ b/hassio/addons/model.py @@ -57,6 +57,7 @@ from ..const import ( ATTR_UDEV, ATTR_URL, ATTR_VERSION, + ATTR_VIDEO, ATTR_WEBUI, SECURITY_DEFAULT, SECURITY_DISABLE, @@ -393,6 +394,11 @@ class AddonModel(CoreSysAttributes): """Return True if the add-on access to audio.""" return self.data[ATTR_AUDIO] + @property + def with_video(self) -> bool: + """Return True if the add-on access to video.""" + return self.data[ATTR_VIDEO] + @property def homeassistant_version(self) -> Optional[str]: """Return min Home Assistant version they needed by Add-on.""" diff --git a/hassio/addons/validate.py b/hassio/addons/validate.py index dd2f524db..ec42060f7 100644 --- a/hassio/addons/validate.py +++ b/hassio/addons/validate.py @@ -77,6 +77,7 @@ from ..const import ( ATTR_USER, ATTR_UUID, ATTR_VERSION, + ATTR_VIDEO, ATTR_WEBUI, BOOT_AUTO, BOOT_MANUAL, @@ -219,6 +220,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema( vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(), vol.Optional(ATTR_FULL_ACCESS, default=False): vol.Boolean(), vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(), + vol.Optional(ATTR_VIDEO, default=False): vol.Boolean(), vol.Optional(ATTR_GPIO, default=False): vol.Boolean(), vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(), vol.Optional(ATTR_KERNEL_MODULES, default=False): vol.Boolean(), diff --git a/hassio/api/addons.py b/hassio/api/addons.py index 63eb4226e..894b03001 100644 --- a/hassio/api/addons.py +++ b/hassio/api/addons.py @@ -82,6 +82,7 @@ from ..const import ( ATTR_UDEV, ATTR_URL, ATTR_VERSION, + ATTR_VIDEO, ATTR_WEBUI, BOOT_AUTO, BOOT_MANUAL, @@ -238,6 +239,7 @@ class APIAddons(CoreSysAttributes): ATTR_DEVICETREE: addon.with_devicetree, ATTR_UDEV: addon.with_udev, ATTR_DOCKER_API: addon.access_docker_api, + ATTR_VIDEO: addon.with_video, ATTR_AUDIO: addon.with_audio, ATTR_AUDIO_INPUT: None, ATTR_AUDIO_OUTPUT: None, diff --git a/hassio/const.py b/hassio/const.py index 91a7c54b6..20651c875 100644 --- a/hassio/const.py +++ b/hassio/const.py @@ -149,6 +149,7 @@ ATTR_TYPE = "type" ATTR_TIMEOUT = "timeout" ATTR_AUTO_UPDATE = "auto_update" ATTR_CUSTOM = "custom" +ATTR_VIDEO = "video" ATTR_AUDIO = "audio" ATTR_AUDIO_INPUT = "audio_input" ATTR_AUDIO_OUTPUT = "audio_output" diff --git a/hassio/docker/addon.py b/hassio/docker/addon.py index a220492e5..d6d8c4e69 100644 --- a/hassio/docker/addon.py +++ b/hassio/docker/addon.py @@ -147,6 +147,11 @@ class DockerAddon(DockerInterface): for device in serial_devs: devices.append(f"{device}:{device}:rwm") + # Use video devices + if self.addon.with_video: + for device in self.sys_hardware.video_devices: + devices.append(f"{device.path!s}:{device.path!s}:rwm") + # Return None if no devices is present return devices or None diff --git a/hassio/misc/hardware.py b/hassio/misc/hardware.py index 800669650..269d1ca47 100644 --- a/hassio/misc/hardware.py +++ b/hassio/misc/hardware.py @@ -4,13 +4,15 @@ from datetime import datetime import logging from pathlib import Path import re -from typing import Any, Dict, Optional, Set +from typing import Any, Dict, List, Optional, Set +import attr 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__) ASOUND_CARDS: Path = Path("/proc/asound/cards") @@ -26,6 +28,17 @@ GPIO_DEVICES: Path = Path("/sys/class/gpio") SOC_DEVICES: Path = Path("/sys/devices/platform/soc") RE_TTY: re.Pattern = re.compile(r"tty[A-Z]+") +RE_VIDEO_DEVICES = re.compile(r"^(?:vchiq|cec)") + + +@attr.s(frozen=True) +class Device: + """Represent a device.""" + + name: str = attr.ib() + path: Path = attr.ib() + links: List[Path] = attr.ib() + class Hardware: """Representation of an interface to procfs, sysfs and udev.""" @@ -34,6 +47,33 @@ class Hardware: """Init hardware object.""" self.context = pyudev.Context() + @property + def devices(self) -> List[Device]: + """Return a list of all available devices.""" + dev_list: List[Device] = [] + + # Exctract all devices + for device in self.context.list_devices(): + dev_list.append( + Device(device.sys_name), + Path(device.device_node), + [Path(node) for node in device.device_links], + ) + + return dev_list + + @property + def video_devices(self) -> List[Device]: + """Return all available video devices.""" + dev_list: List[Device] = [] + + for device in self.devices: + if not RE_VIDEO_DEVICES.match(device.name): + continue + dev_list.append(device) + + return dev_list + @property def serial_devices(self) -> Set[str]: """Return all serial and connected devices."""