Update API for hass api v2 (#14)

* Update API for hass api v2

* fix lint

* Refactory the old version of host_control

* cleanup

* Cleanup name inside addons/data

* Cleanup name inside addons/data p2

* Rename api list

* Fix path bug

* Fix wrong config set
This commit is contained in:
Pascal Vizeli 2017-04-26 11:15:56 +02:00 committed by GitHub
parent a0f17ffd1d
commit 76cead72e8
15 changed files with 185 additions and 169 deletions

38
API.md
View File

@ -2,7 +2,7 @@
## HassIO REST API
Interface for HomeAssistant to controll things from supervisor.
Interface for HomeAssistant to control things from supervisor.
On error:
```json
@ -29,13 +29,13 @@ On success
```json
{
"version": "INSTALL_VERSION",
"current": "CURRENT_VERSION",
"beta": "true|false",
"last_version": "CURRENT_VERSION",
"beta_channel": "true|false",
"addons": [
{
"name": "xy bla",
"slug": "xy",
"version": "CURRENT_VERSION",
"version": "LAST_VERSION",
"installed": "none|INSTALL_VERSION",
"dedicated": "bool",
"description": "description"
@ -55,7 +55,7 @@ Optional:
- `/supervisor/option`
```json
{
"beta": "true|false"
"beta_channel": "true|false"
}
```
@ -74,13 +74,13 @@ Output the raw docker log
- `/host/reboot`
- `/host/info`
See HostControll info command.
See HostControl info command.
```json
{
"os": "",
"type": "",
"version": "",
"current": "",
"level": "",
"last_version": "",
"features": ["shutdown", "reboot", "update", "network_info", "network_control"],
"hostname": "",
}
```
@ -116,7 +116,7 @@ Optional:
```json
{
"version": "INSTALL_VERSION",
"current": "CURRENT_VERSION"
"last_version": "LAST_VERSION"
}
```
@ -138,7 +138,7 @@ Output the raw docker log
```json
{
"version": "VERSION",
"current": "CURRENT_VERSION",
"last_version": "LAST_VERSION",
"state": "started|stopped",
"boot": "auto|manual",
"options": {},
@ -149,7 +149,7 @@ Output the raw docker log
```json
{
"boot": "auto|manual",
"options": {},
"options": {},
}
```
@ -179,14 +179,14 @@ Optional:
Output the raw docker log
## Host Controll
## Host Control
Communicate over unix socket with a host daemon.
- commands
```
# info
-> {'os', 'version', 'current', 'level', 'hostname'}
-> {'type', 'version', 'last_version', 'features', 'hostname'}
# reboot
# shutdown
# host-update [v]
@ -200,10 +200,12 @@ Communicate over unix socket with a host daemon.
# network int route xy
```
level:
- 1: power functions
- 2: host update
- 4: network functions
features:
- shutdown
- reboot
- update
- network_info
- network_control
Answer:
```

View File

@ -1,7 +1,7 @@
# HassIO
First private cloud solution for home automation.
It is a docker image (supervisor) they manage HomeAssistant docker and give a interface to controll itself over UI. It have a own eco system with addons to extend the functionality in a easy way.
It is a docker image (supervisor) they manage HomeAssistant docker and give a interface to control itself over UI. It have a own eco system with addons to extend the functionality in a easy way.
[HassIO-Addons](https://github.com/pvizeli/hassio-addons) | [HassIO-Build](https://github.com/pvizeli/hassio-build)
@ -33,8 +33,8 @@ After extracting the archive, flash it to a drive using [Etcher](https://etcher.
## History
- **0.1**: First techpreview with dumy supervisor (ResinOS 2.0.0-RC5)
- **0.2**: Fix some bugs and update it to HassIO 0.2
- **0.3**: Update HostControll and feature for HassIO 0.3 (ResinOS 2.0.0 / need reflash)
- **0.4**: Update HostControll and bring resinos OTA (resinhub) back (ResinOS 2.0.0-rev3)
- **0.3**: Update HostControl and feature for HassIO 0.3 (ResinOS 2.0.0 / need reflash)
- **0.4**: Update HostControl and bring resinos OTA (resinhub) back (ResinOS 2.0.0-rev3)
## Configuring the image
You can configure the WiFi network that the image should connect to after flashing using [`resin-device-toolbox`](https://resinos.io/docs/raspberrypi3/gettingstarted/#install-resin-device-toolbox).

View File

@ -78,7 +78,7 @@ class AddonManager(AddonsData):
addon_docker = DockerAddon(
self.config, self.loop, self.dock, self, addon)
version = version or self.get_version(addon)
version = version or self.get_last_version(addon)
if not await addon_docker.install(version):
return False
@ -144,7 +144,7 @@ class AddonManager(AddonsData):
_LOGGER.error("No docker found for addon %s", addon)
return False
version = version or self.get_version(addon)
version = version or self.get_last_version(addon)
is_running = self.dockers[addon].is_running()
# update

View File

@ -28,7 +28,7 @@ class AddonsData(Config):
"""Initialize data holder."""
super().__init__(FILE_HASSIO_ADDONS)
self.config = config
self._addons_data = self._data.get(SYSTEM, {})
self._system_data = self._data.get(SYSTEM, {})
self._user_data = self._data.get(USER, {})
self._current_data = {}
self.arch = None
@ -37,7 +37,7 @@ class AddonsData(Config):
"""Store data to config file."""
self._data = {
USER: self._user_data,
SYSTEM: self._addons_data,
SYSTEM: self._system_data,
}
super().save()
@ -69,13 +69,13 @@ class AddonsData(Config):
@property
def list_installed(self):
"""Return a list of installed addons."""
return set(self._addons_data.keys())
return set(self._system_data.keys())
@property
def list(self):
"""Return a list of available addons."""
def list_api(self):
"""Return a list of available addons for api."""
data = []
all_addons = {**self._addons_data, **self._current_data}
all_addons = {**self._system_data, **self._current_data}
dedicated = self.list_removed
for addon, values in all_addons.items():
@ -95,12 +95,12 @@ class AddonsData(Config):
def list_startup(self, start_type):
"""Get list of installed addon with need start by type."""
addon_list = set()
for addon in self._addons_data.keys():
for addon in self._system_data.keys():
if self.get_boot(addon) != BOOT_AUTO:
continue
try:
if self._addons_data[addon][ATTR_STARTUP] == start_type:
if self._system_data[addon][ATTR_STARTUP] == start_type:
addon_list.add(addon)
except KeyError:
_LOGGER.warning("Orphaned addon detect %s", addon)
@ -112,7 +112,7 @@ class AddonsData(Config):
def list_removed(self):
"""Return local addons they not support from repo."""
addon_list = set()
for addon in self._addons_data.keys():
for addon in self._system_data.keys():
if addon not in self._current_data:
addon_list.add(addon)
@ -120,22 +120,19 @@ class AddonsData(Config):
def exists_addon(self, addon):
"""Return True if a addon exists."""
return addon in self._current_data or addon in self._addons_data
return addon in self._current_data or addon in self._system_data
def is_installed(self, addon):
"""Return True if a addon is installed."""
return addon in self._addons_data
return addon in self._system_data
def version_installed(self, addon):
"""Return installed version."""
if ATTR_VERSION not in self._user_data[addon]:
return self._addons_data[addon][ATTR_VERSION]
return self._user_data[addon][ATTR_VERSION]
def set_addon_install(self, addon, version):
"""Set addon as installed."""
self._addons_data[addon] = self._current_data[addon]
self._system_data[addon] = self._current_data[addon]
self._user_data[addon] = {
ATTR_OPTIONS: {},
ATTR_VERSION: version,
@ -144,13 +141,13 @@ class AddonsData(Config):
def set_addon_uninstall(self, addon):
"""Set addon as uninstalled."""
self._addons_data.pop(addon, None)
self._system_data.pop(addon, None)
self._user_data.pop(addon, None)
self.save()
def set_addon_update(self, addon, version):
"""Update version of addon."""
self._addons_data[addon] = self._current_data[addon]
self._system_data[addon] = self._current_data[addon]
self._user_data[addon][ATTR_VERSION] = version
self.save()
@ -167,7 +164,7 @@ class AddonsData(Config):
def get_options(self, addon):
"""Return options with local changes."""
return {
**self._addons_data[addon][ATTR_OPTIONS],
**self._system_data[addon][ATTR_OPTIONS],
**self._user_data[addon][ATTR_OPTIONS],
}
@ -176,17 +173,17 @@ class AddonsData(Config):
if ATTR_BOOT in self._user_data[addon]:
return self._user_data[addon][ATTR_BOOT]
return self._addons_data[addon][ATTR_BOOT]
return self._system_data[addon][ATTR_BOOT]
def get_name(self, addon):
"""Return name of addon."""
return self._addons_data[addon][ATTR_NAME]
return self._system_data[addon][ATTR_NAME]
def get_description(self, addon):
"""Return description of addon."""
return self._addons_data[addon][ATTR_DESCRIPTON]
return self._system_data[addon][ATTR_DESCRIPTON]
def get_version(self, addon):
def get_last_version(self, addon):
"""Return version of addon."""
if addon not in self._current_data:
return self.version_installed(addon)
@ -194,11 +191,11 @@ class AddonsData(Config):
def get_ports(self, addon):
"""Return ports of addon."""
return self._addons_data[addon].get(ATTR_PORTS)
return self._system_data[addon].get(ATTR_PORTS)
def get_image(self, addon):
"""Return image name of addon."""
addon_data = self._addons_data.get(addon, self._current_data[addon])
addon_data = self._system_data.get(addon, self._current_data[addon])
if ATTR_IMAGE not in addon_data:
return "{}/{}-addon-{}".format(DOCKER_REPO, self.arch, addon)
@ -207,11 +204,11 @@ class AddonsData(Config):
def need_config(self, addon):
"""Return True if config map is needed."""
return self._addons_data[addon][ATTR_MAP_CONFIG]
return self._system_data[addon][ATTR_MAP_CONFIG]
def need_ssl(self, addon):
"""Return True if ssl map is needed."""
return self._addons_data[addon][ATTR_MAP_SSL]
return self._system_data[addon][ATTR_MAP_SSL]
def path_data(self, addon):
"""Return addon data path inside supervisor."""
@ -241,7 +238,7 @@ class AddonsData(Config):
def get_schema(self, addon):
"""Create a schema for addon options."""
raw_schema = self._addons_data[addon][ATTR_SCHEMA]
raw_schema = self._system_data[addon][ATTR_SCHEMA]
schema = vol.Schema(vol.All(dict, validate_options(raw_schema)))
return schema

View File

@ -25,26 +25,26 @@ class RestAPI(object):
self._handler = None
self.server = None
def register_host(self, host_controll):
"""Register hostcontroll function."""
api_host = APIHost(self.config, self.loop, host_controll)
def register_host(self, host_control):
"""Register hostcontrol function."""
api_host = APIHost(self.config, self.loop, host_control)
self.webapp.router.add_get('/host/info', api_host.info)
self.webapp.router.add_get('/host/reboot', api_host.reboot)
self.webapp.router.add_get('/host/shutdown', api_host.shutdown)
self.webapp.router.add_get('/host/update', api_host.update)
def register_network(self, host_controll):
def register_network(self, host_control):
"""Register network function."""
api_net = APINetwork(self.config, self.loop, host_controll)
api_net = APINetwork(self.config, self.loop, host_control)
self.webapp.router.add_get('/network/info', api_net.info)
self.webapp.router.add_get('/network/options', api_net.options)
def register_supervisor(self, supervisor, addons):
def register_supervisor(self, supervisor, addons, host_control):
"""Register supervisor function."""
api_supervisor = APISupervisor(
self.config, self.loop, supervisor, addons)
self.config, self.loop, supervisor, addons, host_control)
self.webapp.router.add_get('/supervisor/ping', api_supervisor.ping)
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)

View File

@ -7,7 +7,7 @@ from voluptuous.humanize import humanize_error
from .util import api_process, api_process_raw, api_validate
from ..const import (
ATTR_VERSION, ATTR_CURRENT, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS,
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_STATE, ATTR_BOOT, ATTR_OPTIONS,
STATE_STOPPED, STATE_STARTED, BOOT_AUTO, BOOT_MANUAL)
_LOGGER = logging.getLogger(__name__)
@ -47,14 +47,13 @@ class APIAddons(object):
"""Return addon information."""
addon = self._extract_addon(request)
info = {
return {
ATTR_VERSION: self.addons.version_installed(addon),
ATTR_CURRENT: self.addons.get_version(addon),
ATTR_LAST_VERSION: self.addons.get_last_version(addon),
ATTR_STATE: await self.addons.state(addon),
ATTR_BOOT: self.addons.get_boot(addon),
ATTR_OPTIONS: self.addons.get_options(addon),
}
return info
@api_process
async def options(self, request):
@ -66,12 +65,12 @@ class APIAddons(object):
vol.Optional(ATTR_OPTIONS): options_schema,
})
addon_config = await api_validate(addon_schema, request)
body = await api_validate(addon_schema, request)
if ATTR_OPTIONS in addon_config:
self.addons.set_options(addon, addon_config[ATTR_OPTIONS])
if ATTR_BOOT in addon_config:
self.addons.set_options(addon, addon_config[ATTR_BOOT])
if ATTR_OPTIONS in body:
self.addons.set_options(addon, body[ATTR_OPTIONS])
if ATTR_BOOT in body:
self.addons.set_boot(addon, body[ATTR_BOOT])
return True
@ -81,7 +80,7 @@ class APIAddons(object):
body = await api_validate(SCHEMA_VERSION, request)
addon = self._extract_addon(request, check_installed=False)
version = body.get(
ATTR_VERSION, self.addons.get_version(addon))
ATTR_VERSION, self.addons.get_last_version(addon))
return await asyncio.shield(
self.addons.install(addon, version), loop=self.loop)
@ -130,7 +129,7 @@ class APIAddons(object):
body = await api_validate(SCHEMA_VERSION, request)
addon = self._extract_addon(request)
version = body.get(
ATTR_VERSION, self.addons.get_version(addon))
ATTR_VERSION, self.addons.get_last_version(addon))
if version == self.addons.version_installed(addon):
raise RuntimeError("Version is already in use")

View File

@ -5,7 +5,7 @@ import logging
import voluptuous as vol
from .util import api_process, api_process_raw, api_validate
from ..const import ATTR_VERSION, ATTR_CURRENT
from ..const import ATTR_VERSION, ATTR_LAST_VERSION
_LOGGER = logging.getLogger(__name__)
@ -28,7 +28,7 @@ class APIHomeAssistant(object):
"""Return host information."""
info = {
ATTR_VERSION: self.homeassistant.version,
ATTR_CURRENT: self.config.current_homeassistant,
ATTR_LAST_VERSION: self.config.last_homeassistant,
}
return info
@ -37,7 +37,7 @@ class APIHomeAssistant(object):
async def update(self, request):
"""Update host OS."""
body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.config.current_homeassistant)
version = body.get(ATTR_VERSION, self.config.last_homeassistant)
if self.homeassistant.in_progress:
raise RuntimeError("Other task is in progress")

View File

@ -3,13 +3,12 @@ import logging
import voluptuous as vol
from .util import api_process_hostcontroll, api_process, api_validate
from ..const import ATTR_VERSION
from .util import api_process_hostcontrol, api_process, api_validate
from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_TYPE, ATTR_HOSTNAME, ATTR_FEATURES)
_LOGGER = logging.getLogger(__name__)
UNKNOWN = 'unknown'
SCHEMA_VERSION = vol.Schema({
vol.Optional(ATTR_VERSION): vol.Coerce(str),
})
@ -18,44 +17,40 @@ SCHEMA_VERSION = vol.Schema({
class APIHost(object):
"""Handle rest api for host functions."""
def __init__(self, config, loop, host_controll):
def __init__(self, config, loop, host_control):
"""Initialize host rest api part."""
self.config = config
self.loop = loop
self.host_controll = host_controll
self.host_control = host_control
@api_process
async def info(self, request):
"""Return host information."""
if not self.host_controll.active:
info = {
'os': UNKNOWN,
'version': UNKNOWN,
'current': UNKNOWN,
'level': 0,
'hostname': UNKNOWN,
}
return info
return {
ATTR_TYPE: self.host_control.type,
ATTR_VERSION: self.host_control.version,
ATTR_LAST_VERSION: self.host_control.last,
ATTR_FEATURES: self.host_control.features,
ATTR_HOSTNAME: self.host_control.hostname,
}
return await self.host_controll.info()
@api_process_hostcontroll
@api_process_hostcontrol
def reboot(self, request):
"""Reboot host."""
return self.host_controll.reboot()
return self.host_control.reboot()
@api_process_hostcontroll
@api_process_hostcontrol
def shutdown(self, request):
"""Poweroff host."""
return self.host_controll.shutdown()
return self.host_control.shutdown()
@api_process_hostcontroll
@api_process_hostcontrol
async def update(self, request):
"""Update host OS."""
body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION)
if version == self.host_controll.version:
if version == self.host_control.version:
raise RuntimeError("Version is already in use")
return await self.host_controll.host_update(version=version)
return await self.host_control.update(version=version)

View File

@ -1,7 +1,7 @@
"""Init file for HassIO network rest api."""
import logging
from .util import api_process_hostcontroll
from .util import api_process_hostcontrol
_LOGGER = logging.getLogger(__name__)
@ -9,18 +9,18 @@ _LOGGER = logging.getLogger(__name__)
class APINetwork(object):
"""Handle rest api for network functions."""
def __init__(self, config, loop, host_controll):
def __init__(self, config, loop, host_control):
"""Initialize network rest api part."""
self.config = config
self.loop = loop
self.host_controll = host_controll
self.host_control = host_control
@api_process_hostcontroll
@api_process_hostcontrol
def info(self, request):
"""Show network settings."""
pass
@api_process_hostcontroll
@api_process_hostcontrol
def options(self, request):
"""Edit network settings."""
pass

View File

@ -6,13 +6,14 @@ import voluptuous as vol
from .util import api_process, api_process_raw, api_validate
from ..const import (
ATTR_ADDONS, ATTR_VERSION, ATTR_CURRENT, ATTR_BETA, HASSIO_VERSION)
ATTR_ADDONS, ATTR_VERSION, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL,
HASSIO_VERSION)
_LOGGER = logging.getLogger(__name__)
SCHEMA_OPTIONS = vol.Schema({
# pylint: disable=no-value-for-parameter
vol.Optional(ATTR_BETA): vol.Boolean(),
vol.Optional(ATTR_BETA_CHANNEL): vol.Boolean(),
})
SCHEMA_VERSION = vol.Schema({
@ -23,12 +24,13 @@ SCHEMA_VERSION = vol.Schema({
class APISupervisor(object):
"""Handle rest api for supervisor functions."""
def __init__(self, config, loop, supervisor, addons):
def __init__(self, config, loop, supervisor, addons, host_control):
"""Initialize supervisor rest api part."""
self.config = config
self.loop = loop
self.supervisor = supervisor
self.addons = addons
self.host_control = host_control
@api_process
async def ping(self, request):
@ -38,21 +40,20 @@ class APISupervisor(object):
@api_process
async def info(self, request):
"""Return host information."""
info = {
return {
ATTR_VERSION: HASSIO_VERSION,
ATTR_CURRENT: self.config.current_hassio,
ATTR_BETA: self.config.upstream_beta,
ATTR_ADDONS: self.addons.list,
ATTR_LAST_VERSION: self.config.last_hassio,
ATTR_BETA_CHANNEL: self.config.upstream_beta,
ATTR_ADDONS: self.addons.list_api,
}
return info
@api_process
async def options(self, request):
"""Set supervisor options."""
body = await api_validate(SCHEMA_OPTIONS, request)
if ATTR_BETA in body:
self.config.upstream_beta = body[ATTR_BETA]
if ATTR_BETA_CHANNEL in body:
self.config.upstream_beta = body[ATTR_BETA_CHANNEL]
return self.config.save()
@ -60,7 +61,7 @@ class APISupervisor(object):
async def update(self, request):
"""Update supervisor OS."""
body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.config.current_hassio)
version = body.get(ATTR_VERSION, self.config.last_hassio)
if version == self.supervisor.version:
raise RuntimeError("Version is already in use")
@ -71,7 +72,10 @@ class APISupervisor(object):
@api_process
async def reload(self, request):
"""Reload addons, config ect."""
tasks = [self.addons.reload(), self.config.fetch_update_infos()]
tasks = [
self.addons.reload(), self.config.fetch_update_infos(),
self.host_control.load()
]
results, _ = await asyncio.shield(
asyncio.wait(tasks, loop=self.loop), loop=self.loop)

View File

@ -39,11 +39,11 @@ def api_process(method):
return wrap_api
def api_process_hostcontroll(method):
"""Wrap HostControll calls to rest api."""
async def wrap_hostcontroll(api, *args, **kwargs):
def api_process_hostcontrol(method):
"""Wrap HostControl calls to rest api."""
async def wrap_hostcontrol(api, *args, **kwargs):
"""Return host information."""
if not api.host_controll.active:
if not api.host_control.active:
raise HTTPServiceUnavailable()
try:
@ -59,7 +59,7 @@ def api_process_hostcontroll(method):
return api_return_ok()
return api_return_error()
return wrap_hostcontroll
return wrap_hostcontrol
def api_process_raw(method):

View File

@ -10,10 +10,10 @@ _LOGGER = logging.getLogger(__name__)
HOMEASSISTANT_CONFIG = "{}/homeassistant"
HOMEASSISTANT_IMAGE = 'homeassistant_image'
HOMEASSISTANT_CURRENT = 'homeassistant_current'
HOMEASSISTANT_LAST = 'homeassistant_last'
HASSIO_SSL = "{}/ssl"
HASSIO_CURRENT = 'hassio_current'
HASSIO_LAST = 'hassio_last'
HASSIO_CLEANUP = 'hassio_cleanup'
ADDONS_REPO = "{}/addons"
@ -67,13 +67,13 @@ class CoreConfig(Config):
async def fetch_update_infos(self):
"""Read current versions from web."""
current = await fetch_current_versions(
last = await fetch_current_versions(
self.websession, beta=self.upstream_beta)
if current:
if last:
self._data.update({
HOMEASSISTANT_CURRENT: current.get('homeassistant_tag'),
HASSIO_CURRENT: current.get('hassio_tag'),
HOMEASSISTANT_LAST: last.get('homeassistant'),
HASSIO_LAST: last.get('hassio'),
})
self.save()
return True
@ -120,14 +120,14 @@ class CoreConfig(Config):
return self._data.get(HOMEASSISTANT_IMAGE)
@property
def current_homeassistant(self):
def last_homeassistant(self):
"""Actual version of homeassistant."""
return self._data.get(HOMEASSISTANT_CURRENT)
return self._data.get(HOMEASSISTANT_LAST)
@property
def current_hassio(self):
def last_hassio(self):
"""Actual version of hassio."""
return self._data.get(HASSIO_CURRENT)
return self._data.get(HASSIO_LAST)
@property
def path_hassio_docker(self):

View File

@ -1,5 +1,5 @@
"""Const file for HassIO."""
HASSIO_VERSION = '0.14'
HASSIO_VERSION = '0.15'
URL_HASSIO_VERSION = \
'https://raw.githubusercontent.com/pvizeli/hassio/master/version.json'
@ -31,10 +31,13 @@ JSON_MESSAGE = 'message'
RESULT_ERROR = 'error'
RESULT_OK = 'ok'
ATTR_HOSTNAME = 'hostname'
ATTR_TYPE = 'type'
ATTR_FEATURES = 'features'
ATTR_ADDONS = 'addons'
ATTR_VERSION = 'version'
ATTR_CURRENT = 'current'
ATTR_BETA = 'beta'
ATTR_LAST_VERSION = 'last_version'
ATTR_BETA_CHANNEL = 'beta_channel'
ATTR_NAME = 'name'
ATTR_SLUG = 'slug'
ATTR_DESCRIPTON = 'description'

View File

@ -8,7 +8,7 @@ import docker
from . import bootstrap
from .addons import AddonManager
from .api import RestAPI
from .host_controll import HostControll
from .host_control import HostControl
from .const import (
SOCKET_DOCKER, RUN_UPDATE_INFO_TASKS, RUN_RELOAD_ADDONS_TASKS,
RUN_UPDATE_SUPERVISOR_TASKS, STARTUP_AFTER, STARTUP_BEFORE)
@ -40,8 +40,8 @@ class HassIO(object):
self.homeassistant = DockerHomeAssistant(
self.config, self.loop, self.dock)
# init HostControll
self.host_controll = HostControll(self.loop)
# init HostControl
self.host_control = HostControl(self.loop)
# init addon system
self.addons = AddonManager(self.config, self.loop, self.dock)
@ -55,20 +55,19 @@ class HassIO(object):
# set api endpoint
self.config.api_endpoint = await get_local_ip(self.loop)
# hostcontroll
host_info = await self.host_controll.info()
if host_info:
self.host_controll.version = host_info.get('version')
_LOGGER.info(
"Connected to HostControll. OS: %s Version: %s Hostname: %s "
"Feature-lvl: %d", host_info.get('os'),
host_info.get('version'), host_info.get('hostname'),
host_info.get('level', 0))
# hostcontrol
await self.host_control.load()
_LOGGER.info(
"Connected to HostControl. Type: %s Version: %s Hostname: %s "
"Features: %s", self.host_control.type,
self.host_control.version, self.host_control.hostname,
self.host_control.features)
# rest api views
self.api.register_host(self.host_controll)
self.api.register_network(self.host_controll)
self.api.register_supervisor(self.supervisor, self.addons)
self.api.register_host(self.host_control)
self.api.register_network(self.host_control)
self.api.register_supervisor(
self.supervisor, self.addons, self.host_control)
self.api.register_homeassistant(self.homeassistant)
self.api.register_addons(self.addons)
@ -130,10 +129,10 @@ class HassIO(object):
"""Install a homeassistant docker container."""
while True:
# read homeassistant tag and install it
if not self.config.current_homeassistant:
if not self.config.last_homeassistant:
await self.config.fetch_update_infos()
tag = self.config.current_homeassistant
tag = self.config.last_homeassistant
if tag and await self.homeassistant.install(tag):
break
_LOGGER.warning("Error on setup HomeAssistant. Retry in 60.")
@ -144,9 +143,9 @@ class HassIO(object):
async def _hassio_update(self):
"""Check and run update of supervisor hassio."""
if self.config.current_hassio == self.supervisor.version:
if self.config.last_hassio == self.supervisor.version:
return
_LOGGER.info(
"Found new HassIO version %s.", self.config.current_hassio)
await self.supervisor.update(self.config.current_hassio)
"Found new HassIO version %s.", self.config.last_hassio)
await self.supervisor.update(self.config.last_hassio)

View File

@ -1,4 +1,4 @@
"""Host controll for HassIO."""
"""Host control for HassIO."""
import asyncio
import json
import logging
@ -7,25 +7,34 @@ import stat
import async_timeout
from .const import SOCKET_HC
from .const import (
SOCKET_HC, ATTR_LAST_VERSION, ATTR_VERSION, ATTR_TYPE, ATTR_FEATURES,
ATTR_HOSTNAME)
_LOGGER = logging.getLogger(__name__)
TIMEOUT = 15
UNKNOWN = 'unknown'
LEVEL_POWER = 1
LEVEL_UPDATE_HOST = 2
LEVEL_NETWORK = 4
FEATURES_SHUTDOWN = 'shutdown'
FEATURES_REBOOT = 'reboot'
FEATURES_UPDATE = 'update'
FEATURES_NETWORK_INFO = 'network_info'
FEATURES_NETWORK_CONTROL = 'network_control'
class HostControll(object):
"""Client for host controll."""
class HostControl(object):
"""Client for host control."""
def __init__(self, loop):
"""Initialize HostControll socket client."""
"""Initialize HostControl socket client."""
self.loop = loop
self.active = False
self.version = None
self.version = UNKNOWN
self.last = UNKNOWN
self.type = UNKNOWN
self.features = []
self.hostname = UNKNOWN
mode = os.stat(SOCKET_HC)[stat.ST_MODE]
if stat.S_ISSOCK(mode):
@ -44,14 +53,14 @@ class HostControll(object):
try:
# send
_LOGGER.info("Send '%s' to HostControll.", command)
_LOGGER.info("Send '%s' to HostControl.", command)
with async_timeout.timeout(TIMEOUT, loop=self.loop):
writer.write("{}\n".format(command).encode())
data = await reader.readline()
response = data.decode()
_LOGGER.debug("Receive from HostControll: %s.", response)
_LOGGER.debug("Receive from HostControl: %s.", response)
if response == "OK":
return True
@ -63,20 +72,28 @@ class HostControll(object):
try:
return json.loads(response)
except json.JSONDecodeError:
_LOGGER.warning("Json parse error from HostControll.")
_LOGGER.warning("Json parse error from HostControl.")
except asyncio.TimeoutError:
_LOGGER.error("Timeout from HostControll!")
_LOGGER.error("Timeout from HostControl!")
finally:
writer.close()
def info(self):
"""Return Info from host.
async def load(self):
"""Load Info from host.
Return a coroutine.
"""
return self._send_command("info")
info = await self._send_command("info")
if not info:
return
self.version = info.get(ATTR_VERSION, UNKNOWN)
self.last = info.get(ATTR_LAST_VERSION, UNKNOWN)
self.type = info.get(ATTR_TYPE, UNKNOWN)
self.features = info.get(ATTR_FEATURES, [])
self.hostname = info.get(ATTR_HOSTNAME, UNKNOWN)
def reboot(self):
"""Reboot the host system.
@ -92,11 +109,11 @@ class HostControll(object):
"""
return self._send_command("shutdown")
def host_update(self, version=None):
def update(self, version=None):
"""Update the host system.
Return a coroutine.
"""
if version:
return self._send_command("host-update {}".format(version))
return self._send_command("host-update")
return self._send_command("update {}".format(version))
return self._send_command("update")