mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-18 06:36:30 +00:00
Hardware interface for UI (#116)
* Init hardware object * Update API * Update hardware list * fix api description * fix lint * add hardware to API * fix lint * fix wrong * fix view
This commit is contained in:
parent
fa687e982e
commit
1155ee07e5
18
API.md
18
API.md
@ -226,6 +226,24 @@ Optional:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- GET `/host/hardware`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"serial": ["/dev/xy"],
|
||||||
|
"input": ["Input device name"],
|
||||||
|
"disk": ["/dev/sdax"],
|
||||||
|
"audio": {
|
||||||
|
"CARD_ID": {
|
||||||
|
"name": "xy",
|
||||||
|
"type": "microphone",
|
||||||
|
"devices": {
|
||||||
|
"DEV_ID": "type of device"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Network
|
### Network
|
||||||
|
|
||||||
- GET `/network/info`
|
- GET `/network/info`
|
||||||
|
@ -28,11 +28,12 @@ class RestAPI(object):
|
|||||||
self._handler = None
|
self._handler = None
|
||||||
self.server = None
|
self.server = None
|
||||||
|
|
||||||
def register_host(self, host_control):
|
def register_host(self, host_control, hardware):
|
||||||
"""Register hostcontrol function."""
|
"""Register hostcontrol function."""
|
||||||
api_host = APIHost(self.config, self.loop, host_control)
|
api_host = APIHost(self.config, self.loop, host_control, hardware)
|
||||||
|
|
||||||
self.webapp.router.add_get('/host/info', api_host.info)
|
self.webapp.router.add_get('/host/info', api_host.info)
|
||||||
|
self.webapp.router.add_get('/host/hardware', api_host.hardware)
|
||||||
self.webapp.router.add_post('/host/reboot', api_host.reboot)
|
self.webapp.router.add_post('/host/reboot', api_host.reboot)
|
||||||
self.webapp.router.add_post('/host/shutdown', api_host.shutdown)
|
self.webapp.router.add_post('/host/shutdown', api_host.shutdown)
|
||||||
self.webapp.router.add_post('/host/update', api_host.update)
|
self.webapp.router.add_post('/host/update', api_host.update)
|
||||||
|
@ -7,7 +7,7 @@ import voluptuous as vol
|
|||||||
from .util import api_process_hostcontrol, api_process, api_validate
|
from .util import api_process_hostcontrol, api_process, api_validate
|
||||||
from ..const import (
|
from ..const import (
|
||||||
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_TYPE, ATTR_HOSTNAME, ATTR_FEATURES,
|
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_TYPE, ATTR_HOSTNAME, ATTR_FEATURES,
|
||||||
ATTR_OS)
|
ATTR_OS, ATTR_SERIAL, ATTR_INPUT, ATTR_DISK, ATTR_AUDIO)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -19,11 +19,12 @@ SCHEMA_VERSION = vol.Schema({
|
|||||||
class APIHost(object):
|
class APIHost(object):
|
||||||
"""Handle rest api for host functions."""
|
"""Handle rest api for host functions."""
|
||||||
|
|
||||||
def __init__(self, config, loop, host_control):
|
def __init__(self, config, loop, host_control, hardware):
|
||||||
"""Initialize host rest api part."""
|
"""Initialize host rest api part."""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.host_control = host_control
|
self.host_control = host_control
|
||||||
|
self.local_hw = hardware
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
async def info(self, request):
|
async def info(self, request):
|
||||||
@ -58,3 +59,13 @@ class APIHost(object):
|
|||||||
|
|
||||||
return await asyncio.shield(
|
return await asyncio.shield(
|
||||||
self.host_control.update(version=version), loop=self.loop)
|
self.host_control.update(version=version), loop=self.loop)
|
||||||
|
|
||||||
|
@api_process
|
||||||
|
async def hardware(self, request):
|
||||||
|
"""Return local hardware infos."""
|
||||||
|
return {
|
||||||
|
ATTR_SERIAL: self.local_hw.serial_devices,
|
||||||
|
ATTR_INPUT: self.local_hw.input_devices,
|
||||||
|
ATTR_DISK: self.local_hw.disk_devices,
|
||||||
|
ATTR_AUDIO: self.local_hw.audio_devices,
|
||||||
|
}
|
||||||
|
@ -101,6 +101,10 @@ ATTR_TYPE = 'type'
|
|||||||
ATTR_TIMEOUT = 'timeout'
|
ATTR_TIMEOUT = 'timeout'
|
||||||
ATTR_AUTO_UPDATE = 'auto_update'
|
ATTR_AUTO_UPDATE = 'auto_update'
|
||||||
ATTR_CUSTOM = 'custom'
|
ATTR_CUSTOM = 'custom'
|
||||||
|
ATTR_AUDIO = 'audio'
|
||||||
|
ATTR_INPUT = 'input'
|
||||||
|
ATTR_DISK = 'disk'
|
||||||
|
ATTR_SERIAL = 'serial'
|
||||||
|
|
||||||
STARTUP_INITIALIZE = 'initialize'
|
STARTUP_INITIALIZE = 'initialize'
|
||||||
STARTUP_SYSTEM = 'system'
|
STARTUP_SYSTEM = 'system'
|
||||||
|
@ -14,6 +14,7 @@ from .const import (
|
|||||||
RUN_CLEANUP_API_SESSIONS, STARTUP_SYSTEM, STARTUP_SERVICES,
|
RUN_CLEANUP_API_SESSIONS, STARTUP_SYSTEM, STARTUP_SERVICES,
|
||||||
STARTUP_APPLICATION, STARTUP_INITIALIZE, RUN_RELOAD_SNAPSHOTS_TASKS,
|
STARTUP_APPLICATION, STARTUP_INITIALIZE, RUN_RELOAD_SNAPSHOTS_TASKS,
|
||||||
RUN_UPDATE_ADDONS_TASKS)
|
RUN_UPDATE_ADDONS_TASKS)
|
||||||
|
from .hardware import Hardware
|
||||||
from .homeassistant import HomeAssistant
|
from .homeassistant import HomeAssistant
|
||||||
from .scheduler import Scheduler
|
from .scheduler import Scheduler
|
||||||
from .dock.supervisor import DockerSupervisor
|
from .dock.supervisor import DockerSupervisor
|
||||||
@ -36,6 +37,7 @@ class HassIO(object):
|
|||||||
self.websession = aiohttp.ClientSession(loop=loop)
|
self.websession = aiohttp.ClientSession(loop=loop)
|
||||||
self.scheduler = Scheduler(loop)
|
self.scheduler = Scheduler(loop)
|
||||||
self.api = RestAPI(config, loop)
|
self.api = RestAPI(config, loop)
|
||||||
|
self.hardware = Hardware()
|
||||||
self.dock = docker.DockerClient(
|
self.dock = docker.DockerClient(
|
||||||
base_url="unix:/{}".format(str(SOCKET_DOCKER)), version='auto')
|
base_url="unix:/{}".format(str(SOCKET_DOCKER)), version='auto')
|
||||||
|
|
||||||
@ -81,7 +83,7 @@ class HassIO(object):
|
|||||||
self.host_control.load, RUN_UPDATE_INFO_TASKS)
|
self.host_control.load, RUN_UPDATE_INFO_TASKS)
|
||||||
|
|
||||||
# rest api views
|
# rest api views
|
||||||
self.api.register_host(self.host_control)
|
self.api.register_host(self.host_control, self.hardware)
|
||||||
self.api.register_network(self.host_control)
|
self.api.register_network(self.host_control)
|
||||||
self.api.register_supervisor(
|
self.api.register_supervisor(
|
||||||
self.supervisor, self.snapshots, self.addons, self.host_control,
|
self.supervisor, self.snapshots, self.addons, self.host_control,
|
||||||
|
87
hassio/hardware.py
Normal file
87
hassio/hardware.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
"""Read hardware info from system."""
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pyudev
|
||||||
|
|
||||||
|
from .const import ATTR_NAME, ATTR_TYPE, ATTR_DEVICES
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
ASOUND_CARDS = Path("/proc/asound/cards")
|
||||||
|
RE_CARDS = re.compile(r"(\d+) \[(\w*) *\]: (.*\w)")
|
||||||
|
|
||||||
|
ASOUND_DEVICES = Path("/proc/asound/devices")
|
||||||
|
RE_DEVICES = re.compile(r"\[.*(\d+)- (\d+).*\]: ([\w ]*)")
|
||||||
|
|
||||||
|
|
||||||
|
class Hardware(object):
|
||||||
|
"""Represent a interface to procfs, sysfs and udev."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Init hardware object."""
|
||||||
|
self.context = pyudev.Context()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serial_devices(self):
|
||||||
|
"""Return all serial and connected devices."""
|
||||||
|
dev_list = set()
|
||||||
|
for device in self.context.list_devices(subsystem='tty'):
|
||||||
|
if 'ID_VENDOR' in device:
|
||||||
|
dev_list.add(device.device_node)
|
||||||
|
|
||||||
|
return list(dev_list)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def input_devices(self):
|
||||||
|
"""Return all input devices."""
|
||||||
|
dev_list = set()
|
||||||
|
for device in self.context.list_devices(subsystem='input'):
|
||||||
|
if 'NAME' in device:
|
||||||
|
dev_list.add(device['NAME'].replace('"', ''))
|
||||||
|
|
||||||
|
return list(dev_list)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def disk_devices(self):
|
||||||
|
"""Return all disk devices."""
|
||||||
|
dev_list = set()
|
||||||
|
for device in self.context.list_devices(subsystem='block'):
|
||||||
|
if 'ID_VENDOR' in device:
|
||||||
|
dev_list.add(device.device_node)
|
||||||
|
|
||||||
|
return list(dev_list)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def audio_devices(self):
|
||||||
|
"""Return all available audio interfaces."""
|
||||||
|
try:
|
||||||
|
with ASOUND_CARDS.open('r') as cards_file:
|
||||||
|
cards = cards_file.read()
|
||||||
|
with ASOUND_DEVICES.open('r') as devices_file:
|
||||||
|
devices = devices_file.read()
|
||||||
|
except OSError as err:
|
||||||
|
_LOGGER.error("Can't read asound data -> %s", err)
|
||||||
|
return
|
||||||
|
|
||||||
|
audio_list = {}
|
||||||
|
|
||||||
|
# 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][match.group(2)] = \
|
||||||
|
match.group(3)
|
||||||
|
except KeyError:
|
||||||
|
_LOGGER.warning("Wrong audio device found %s", match.group(0))
|
||||||
|
continue
|
||||||
|
|
||||||
|
return audio_list
|
@ -1 +1 @@
|
|||||||
Subproject commit 1aa387a72f2a8b0c26a005afbba9a8a9fc095e06
|
Subproject commit 0c428134dfb6374b4a120cb729396242b4c2f2d7
|
Loading…
x
Reference in New Issue
Block a user