HassOS support (#522)

* Add support for hassos

* Name command

* Update host.py

* Create hassos.py

* Update const.py

* Update host.py

* Update API.md

* Update const.py

* Update __init__.py

* Update hassos.py

* Update hassos.py

* Update hassos.py

* Update hassos.py

* Update const.py

* Update API.md

* Update hassos.py

* Update hassos.py

* Update API.md

* Update const.py

* Update hassos.py

* Update __init__.py

* fix lint

* Fix lint v2

* remove old function

* fix attribute error

* inittialize hassos

* Fix link

* fix error handling

* Fix handling
This commit is contained in:
Pascal Vizeli 2018-06-22 22:54:03 +02:00 committed by GitHub
parent 408d6eafcc
commit a0c9e5ad26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 168 additions and 37 deletions

41
API.md
View File

@ -227,14 +227,12 @@ return:
```json
{
"hostname": "hostname|null",
"features": ["shutdown", "reboot", "update", "hostname", "services"],
"operating_system": "Hass.io-OS XY|Ubuntu 16.4|null",
"features": ["shutdown", "reboot", "hostname", "services", "hassos"],
"operating_system": "HassOS XY|Ubuntu 16.4|null",
"kernel": "4.15.7|null",
"chassis": "specific|null",
"type": "Hass.io-OS Type|null",
"deployment": "stable|beta|dev|null",
"version": "xy|null",
"last_version": "xy|null",
"cpe": "xy|null",
}
```
@ -246,17 +244,6 @@ return:
}
```
- POST `/host/update`
Optional:
```json
{
"version": "VERSION"
}
```
- POST `/host/reload`
#### Services
@ -280,6 +267,28 @@ Optional:
- POST `/host/service/{unit}/reload`
### HassOS
- GET `/hassos/info`
```json
{
"version": "2.3",
"version_latest": "2.4",
"board": "ova|rpi"
}
```
- POST `/hassos/update`
```json
{
"version": "optional"
}
```
- POST `/hassos/config/sync`
Load host configs from a USB stick.
### Hardware
- GET `/hardware/info`

View File

@ -9,6 +9,7 @@ from .discovery import APIDiscovery
from .homeassistant import APIHomeAssistant
from .hardware import APIHardware
from .host import APIHost
from .hassos import APIHassOS
from .proxy import APIProxy
from .supervisor import APISupervisor
from .snapshots import APISnapshots
@ -37,6 +38,7 @@ class RestAPI(CoreSysAttributes):
"""Register REST API Calls."""
self._register_supervisor()
self._register_host()
self._register_hassos()
self._register_hardware()
self._register_homeassistant()
self._register_proxy()
@ -55,7 +57,6 @@ class RestAPI(CoreSysAttributes):
web.get('/host/info', api_host.info),
web.post('/host/reboot', api_host.reboot),
web.post('/host/shutdown', api_host.shutdown),
web.post('/host/update', api_host.update),
web.post('/host/reload', api_host.reload),
web.get('/host/services', api_host.services),
web.post('/host/services/{service}/stop', api_host.service_stop),
@ -66,6 +67,16 @@ class RestAPI(CoreSysAttributes):
'/host/services/{service}/reload', api_host.service_reload),
])
def _register_hassos(self):
"""Register hassos function."""
api_hassos = APIHassOS()
api_hassos.coresys = self.coresys
self.webapp.add_routes([
web.get('/hassos/info', api_hassos.info),
web.post('/hassos/config/sync', api_hassos.config_sync),
])
def _register_hardware(self):
"""Register hardware function."""
api_hardware = APIHardware()

33
hassio/api/hassos.py Normal file
View File

@ -0,0 +1,33 @@
"""Init file for Hass.io hassos rest api."""
import asyncio
import logging
import voluptuous as vol
from .utils import api_process
from ..const import ATTR_VERSION, ATTR_BOARD, ATTR_VERSION_LATEST
from ..coresys import CoreSysAttributes
_LOGGER = logging.getLogger(__name__)
SCHEMA_VERSION = vol.Schema({
vol.Optional(ATTR_VERSION): vol.Coerce(str),
})
class APIHassOS(CoreSysAttributes):
"""Handle rest api for hassos functions."""
@api_process
async def info(self, request):
"""Return hassos information."""
return {
ATTR_VERSION: self.sys_hassos.version,
ATTR_VERSION_LATEST: self.sys_hassos.version,
ATTR_BOARD: self.sys_hassos.board,
}
@api_process
def config_sync(self, request):
"""Trigger config reload on HassOS."""
return asyncio.shield(self.sys_hassos.config_sync())

View File

@ -6,19 +6,15 @@ import voluptuous as vol
from .utils import api_process, api_validate
from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_HOSTNAME, ATTR_FEATURES, ATTR_KERNEL,
ATTR_TYPE, ATTR_OPERATING_SYSTEM, ATTR_CHASSIS, ATTR_DEPLOYMENT,
ATTR_STATE, ATTR_NAME, ATTR_DESCRIPTON, ATTR_SERVICES)
ATTR_HOSTNAME, ATTR_FEATURES, ATTR_KERNEL, ATTR_OPERATING_SYSTEM,
ATTR_CHASSIS, ATTR_DEPLOYMENT, ATTR_STATE, ATTR_NAME, ATTR_DESCRIPTON,
ATTR_SERVICES, ATTR_CPE)
from ..coresys import CoreSysAttributes
_LOGGER = logging.getLogger(__name__)
SERVICE = 'service'
SCHEMA_VERSION = vol.Schema({
vol.Optional(ATTR_VERSION): vol.Coerce(str),
})
SCHEMA_OPTIONS = vol.Schema({
vol.Optional(ATTR_HOSTNAME): vol.Coerce(str),
})
@ -32,9 +28,7 @@ class APIHost(CoreSysAttributes):
"""Return host information."""
return {
ATTR_CHASSIS: self.sys_host.info.chassis,
ATTR_VERSION: None,
ATTR_LAST_VERSION: None,
ATTR_TYPE: None,
ATTR_CPE: self.sys_host.info.cpe,
ATTR_FEATURES: self.sys_host.supperted_features,
ATTR_HOSTNAME: self.sys_host.info.hostname,
ATTR_OPERATING_SYSTEM: self.sys_host.info.operating_system,
@ -67,13 +61,6 @@ class APIHost(CoreSysAttributes):
"""Reload host data."""
return asyncio.shield(self.sys_host.reload())
@api_process
async def update(self, request):
"""Update host OS."""
pass
# body = await api_validate(SCHEMA_VERSION, request)
# version = body.get(ATTR_VERSION, self.sys_host.last_version)
@api_process
async def services(self, request):
"""Return list of available services."""

View File

@ -21,6 +21,7 @@ from .services import ServiceManager
from .services import Discovery
from .host import HostManager
from .dbus import DBusManager
from .hassos import HassOS
_LOGGER = logging.getLogger(__name__)
@ -47,6 +48,7 @@ def initialize_coresys(loop):
coresys.services = ServiceManager(coresys)
coresys.discovery = Discovery(coresys)
coresys.dbus = DBusManager(coresys)
coresys.hassos = HassOS(coresys)
# bootstrap config
initialize_system_data(coresys)

View File

@ -71,6 +71,7 @@ ATTR_SOURCE = 'source'
ATTR_FEATURES = 'features'
ATTR_ADDONS = 'addons'
ATTR_VERSION = 'version'
ATTR_VERSION_LATEST = 'version_latest'
ATTR_AUTO_UART = 'auto_uart'
ATTR_LAST_BOOT = 'last_boot'
ATTR_LAST_VERSION = 'last_version'
@ -166,6 +167,8 @@ ATTR_BRANCH = 'branch'
ATTR_KERNEL = 'kernel'
ATTR_APPARMOR = 'apparmor'
ATTR_DEVICETREE = 'devicetree'
ATTR_CPE = 'cpe'
ATTR_BOARD = 'board'
SERVICE_MQTT = 'mqtt'
@ -216,6 +219,6 @@ SECURITY_DISABLE = 'disable'
FEATURES_SHUTDOWN = 'shutdown'
FEATURES_REBOOT = 'reboot'
FEATURES_UPDATE = 'update'
FEATURES_HASSOS = 'hassos'
FEATURES_HOSTNAME = 'hostname'
FEATURES_SERVICES = 'services'

View File

@ -32,6 +32,9 @@ class HassIO(CoreSysAttributes):
# Load Host
await self.sys_host.load()
# Load HassOS
await self.sys_hassos.load()
# Load Supervisor
await self.sys_supervisor.load()

View File

@ -43,6 +43,7 @@ class CoreSys:
self._tasks = None
self._host = None
self._dbus = None
self._hassos = None
self._services = None
self._discovery = None
@ -249,6 +250,18 @@ class CoreSys:
raise RuntimeError("HostManager already set!")
self._host = value
@property
def hassos(self):
"""Return HassOS object."""
return self._hassos
@hassos.setter
def hassos(self, value):
"""Set a HassOS object."""
if self._hassos:
raise RuntimeError("HassOS already set!")
self._hassos = value
def run_in_executor(self, funct, *args):
"""Wrapper for executor pool."""
return self._loop.run_in_executor(None, funct, *args)

65
hassio/hassos.py Normal file
View File

@ -0,0 +1,65 @@
"""HassOS support on supervisor."""
import logging
from cpe import CPE
from .coresys import CoreSysAttributes
from .exceptions import HassioNotSupportedError
_LOGGER = logging.getLogger(__name__)
class HassOS(CoreSysAttributes):
"""HassOS interface inside HassIO."""
def __init__(self, coresys):
"""Initialize HassOS handler."""
self.coresys = coresys
self._available = False
self._version = None
self._board = None
@property
def available(self):
"""Return True, if HassOS on host."""
return self._available
@property
def version(self):
"""Return version of HassOS."""
return self._version
@property
def board(self):
"""Return board name."""
return self._board
def _check_host(self):
"""Check if HassOS is availabe."""
if not self.available:
_LOGGER.error("No HassOS availabe")
raise HassioNotSupportedError()
async def load(self):
"""Load HassOS data."""
try:
assert self.sys_host.info.cpe is not None
cpe = CPE(self.sys_host.info.cpe)
assert cpe.get_product()[0] == 'hassos'
except (NotImplementedError, IndexError, AssertionError):
_LOGGER.info("Can't detect HassOS")
return
# Store meta data
self._available = True
self._version = cpe.get_version()[0]
self._board = cpe.get_target_hardware()[0]
_LOGGER.info("Detect HassOS %s on host system", self.version)
def config_sync(self):
"""Trigger a host config reload from usb."""
self._check_host()
_LOGGER.info("Sync config from USB on HassOS.")
return self.sys_host.services.restart('hassos-config.service')

View File

@ -8,7 +8,8 @@ from .control import SystemControl
from .info import InfoCenter
from .services import ServiceManager
from ..const import (
FEATURES_REBOOT, FEATURES_SHUTDOWN, FEATURES_HOSTNAME, FEATURES_SERVICES)
FEATURES_REBOOT, FEATURES_SHUTDOWN, FEATURES_HOSTNAME, FEATURES_SERVICES,
FEATURES_HASSOS)
from ..coresys import CoreSysAttributes
from ..exceptions import HassioError
@ -67,6 +68,9 @@ class HostManager(CoreSysAttributes):
if self.sys_dbus.hostname.is_connected:
features.append(FEATURES_HOSTNAME)
if self.sys_hassos.available:
features.append(FEATURES_HASSOS)
return features
async def reload(self):

View File

@ -49,6 +49,7 @@ setup(
'gitpython==2.1.10',
'pytz==2018.4',
'pyudev==0.21.0',
'pycryptodome==3.4.11'
'pycryptodome==3.4.11',
"cpe==1.2.1"
]
)