mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-06-23 18:36:29 +00:00

* Improve gdbus error handling * Fix logging type * Detect no dbus * Fix issue with complex * Update hassio/dbus/__init__.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Update hassio/dbus/hostname.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Update hassio/dbus/rauc.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Update hassio/dbus/systemd.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Fix black
151 lines
4.8 KiB
Python
151 lines
4.8 KiB
Python
"""Read hardware info from system."""
|
|
from datetime import datetime
|
|
import logging
|
|
from pathlib import Path
|
|
import re
|
|
from typing import Any, Dict, Optional, Set
|
|
|
|
import pyudev
|
|
|
|
from ..const import ATTR_DEVICES, ATTR_NAME, ATTR_TYPE, CHAN_ID, CHAN_TYPE
|
|
|
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
|
|
|
ASOUND_CARDS: Path = Path("/proc/asound/cards")
|
|
RE_CARDS: re.Pattern = re.compile(r"(\d+) \[(\w*) *\]: (.*\w)")
|
|
|
|
ASOUND_DEVICES: Path = Path("/proc/asound/devices")
|
|
RE_DEVICES: re.Pattern = re.compile(r"\[.*(\d+)- (\d+).*\]: ([\w ]*)")
|
|
|
|
PROC_STAT: Path = Path("/proc/stat")
|
|
RE_BOOT_TIME: re.Pattern = re.compile(r"btime (\d+)")
|
|
|
|
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]+")
|
|
|
|
|
|
class Hardware:
|
|
"""Representation of an interface to procfs, sysfs and udev."""
|
|
|
|
def __init__(self):
|
|
"""Init hardware object."""
|
|
self.context = pyudev.Context()
|
|
|
|
@property
|
|
def serial_devices(self) -> Set[str]:
|
|
"""Return all serial and connected devices."""
|
|
dev_list: Set[str] = set()
|
|
for device in self.context.list_devices(subsystem="tty"):
|
|
if "ID_VENDOR" in device.properties or RE_TTY.search(device.device_node):
|
|
dev_list.add(device.device_node)
|
|
|
|
return dev_list
|
|
|
|
@property
|
|
def serial_by_id(self) -> Set[str]:
|
|
"""Return all /dev/serial/by-id for serial devices."""
|
|
dev_list: Set[str] = set()
|
|
for device in self.context.list_devices(subsystem="tty"):
|
|
if "ID_VENDOR" in device.properties or RE_TTY.search(device.device_node):
|
|
# Add /dev/serial/by-id devlink for current device
|
|
for dev_link in device.device_links:
|
|
if not dev_link.startswith("/dev/serial/by-id"):
|
|
continue
|
|
dev_list.add(dev_link)
|
|
|
|
return dev_list
|
|
|
|
@property
|
|
def input_devices(self) -> Set[str]:
|
|
"""Return all input devices."""
|
|
dev_list: Set[str] = set()
|
|
for device in self.context.list_devices(subsystem="input"):
|
|
if "NAME" in device.properties:
|
|
dev_list.add(device.properties["NAME"].replace('"', ""))
|
|
|
|
return dev_list
|
|
|
|
@property
|
|
def disk_devices(self) -> Set[str]:
|
|
"""Return all disk devices."""
|
|
dev_list: Set[str] = set()
|
|
for device in self.context.list_devices(subsystem="block"):
|
|
if "ID_NAME" in device.properties:
|
|
dev_list.add(device.device_node)
|
|
|
|
return dev_list
|
|
|
|
@property
|
|
def support_audio(self) -> bool:
|
|
"""Return True if the system have audio support."""
|
|
return bool(self.audio_devices)
|
|
|
|
@property
|
|
def audio_devices(self) -> Dict[str, Any]:
|
|
"""Return all available audio interfaces."""
|
|
if not ASOUND_CARDS.exists():
|
|
_LOGGER.info("No audio devices found")
|
|
return {}
|
|
|
|
try:
|
|
cards = ASOUND_CARDS.read_text()
|
|
devices = ASOUND_DEVICES.read_text()
|
|
except OSError as err:
|
|
_LOGGER.error("Can't read asound data: %s", err)
|
|
return {}
|
|
|
|
audio_list: Dict[str, Any] = {}
|
|
|
|
# parse cards
|
|
for match in RE_CARDS.finditer(cards):
|
|
audio_list[match.group(1)] = {
|
|
ATTR_NAME: match.group(3),
|
|
ATTR_TYPE: match.group(2),
|
|
ATTR_DEVICES: [],
|
|
}
|
|
|
|
# parse devices
|
|
for match in RE_DEVICES.finditer(devices):
|
|
try:
|
|
audio_list[match.group(1)][ATTR_DEVICES].append(
|
|
{CHAN_ID: match.group(2), CHAN_TYPE: match.group(3)}
|
|
)
|
|
except KeyError:
|
|
_LOGGER.warning("Wrong audio device found %s", match.group(0))
|
|
continue
|
|
|
|
return audio_list
|
|
|
|
@property
|
|
def support_gpio(self) -> bool:
|
|
"""Return True if device support GPIOs."""
|
|
return SOC_DEVICES.exists() and GPIO_DEVICES.exists()
|
|
|
|
@property
|
|
def gpio_devices(self) -> Set[str]:
|
|
"""Return list of GPIO interface on device."""
|
|
dev_list: Set[str] = set()
|
|
for interface in GPIO_DEVICES.glob("gpio*"):
|
|
dev_list.add(interface.name)
|
|
|
|
return dev_list
|
|
|
|
@property
|
|
def last_boot(self) -> Optional[str]:
|
|
"""Return last boot time."""
|
|
try:
|
|
with PROC_STAT.open("r") as stat_file:
|
|
stats: str = stat_file.read()
|
|
except OSError as err:
|
|
_LOGGER.error("Can't read stat data: %s", err)
|
|
return None
|
|
|
|
# parse stat file
|
|
found: Optional[re.Match] = RE_BOOT_TIME.search(stats)
|
|
if not found:
|
|
_LOGGER.error("Can't found last boot time!")
|
|
return None
|
|
|
|
return datetime.utcfromtimestamp(int(found.group(1)))
|