diff --git a/hassio_api/README.md b/hassio_api/README.md index 948ef8b24..a62745dfb 100644 --- a/hassio_api/README.md +++ b/hassio_api/README.md @@ -7,7 +7,7 @@ Communicate over unix socket with a host daemon. - commands ``` # info --> {'host', 'version'} +-> {'os', 'version', 'level', 'hostname'} # reboot # shutdown # host-update [v] @@ -22,6 +22,12 @@ Communicate over unix socket with a host daemon. # network int route xy ``` +level: +- 1: power functions +- 2: supervisor update +- 4: host update +- 8: network functions + - Answer ``` {}|OK|ERROR @@ -33,15 +39,13 @@ Interface for HomeAssistant to controll things from supervisor. ### HassIO -- `/update` +- `/supervisor/info` + +- `/supervisor/update` Payload: {'version': '0.XX'} If version is None it read last version from server. ### Host -- `/host/network` -Payload: {'hostname': '', 'mode': 'dhcp|fixed', 'ssid': '', 'ip': '', 'netmask': '', 'gateway': ''} - -- `/host/reboot` - `/host/shutdown` @@ -50,6 +54,13 @@ Payload: {'hostname': '', 'mode': 'dhcp|fixed', 'ssid': '', 'ip': '', 'netmask': - `/host/update` On some device we support host upates. Like ResinOS. +- `/host/network/info` + +- `/host/network/update` +Payload: {'hostname': '', 'mode': 'dhcp|fixed', 'ssid': '', 'ip': '', 'netmask': '', 'gateway': ''} + +- `/host/reboot` + ### HomeAssistant - `/homeassistant/info` diff --git a/hassio_api/hassio/api/__init__.py b/hassio_api/hassio/api/__init__.py new file mode 100644 index 000000000..a2bb2a4c5 --- /dev/null +++ b/hassio_api/hassio/api/__init__.py @@ -0,0 +1,63 @@ +"""Init file for HassIO rest api.""" +import logging + +from aiohttp import web + +from .homeassistant import APIHomeAssistant +from .host import APIHost + +_LOGGER = logging.getLogger(__name__) + + +class RestAPI(object): + """Handle rest api for hassio.""" + + def __init__(self, config, loop): + """Initialize docker base wrapper.""" + self.config = config + self.loop = loop + self.webapp = web.Application(loop=self.loop) + + self._handler = None + self.server = None + + def registerHost(self, host_controll): + """Register hostcontroll function.""" + api_host = APIHost(self.config, self.loop, host_controll) + + 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_host) + self.webapp.router.add_get( + '/host/network/info', api_host.network_info) + self.webapp.router.add_get( + '/host/network/update', api_host.network_update) + + def registerHomeAssistant(self, dock_homeassistant): + """Register homeassistant function.""" + api_hass = APIHomeAssistant(self.config, self.loop, dock_homeassistant) + + self.webapp.router.add_get('/homeassistant/info', api_hass.info) + self.webapp.router.add_get('/homeassistant/update', api_hass.update) + + async def start(self): + """Run rest api webserver.""" + self._handler = self.webapp.make_handler(loop=self.loop) + + try: + self.server = await self.loop.create_server( + self._handler, "0.0.0.0", "80") + except OSError as err: + _LOGGER.fatal("Failed to create HTTP server at 0.0.0.0:80") + + async def stop(self): + """Stop rest api webserver.""" + if self.server: + self.server.close() + await self.server.wait_closed() + await self.webapp.shutdown() + + if self._handler: + await self._handler.finish_connections(60) + await self.webapp.cleanup() diff --git a/hassio_api/hassio/api/host.py b/hassio_api/hassio/api/host.py new file mode 100644 index 000000000..4e25d5adb --- /dev/null +++ b/hassio_api/hassio/api/host.py @@ -0,0 +1,50 @@ +"""Init file for HassIO rest api.""" +import logging + +from aiohttp import web +from aiohttp.web_exceptions import HTTPOk, HTTPMethodNotAllowed + +_LOGGER = logging.getLogger(__name__) + + +class APIHost(object): + """Handle rest api for host functions.""" + + def __init__(self, config, loop, host_controll): + """Initialize docker base wrapper.""" + self.config = config + self.loop = loop + self.host_controll + + async def info(self, request): + """Return host information.""" + host_info = await self.host_controll.info() + if host_info: + return web.json_response(host_info) + raise HTTPMethodNotAllowed() + + async def reboot(self, request): + """Reboot host.""" + if await self.host_controll.reboot(): + raise HTTPOk() + raise HTTPMethodNotAllowed() + + async def shutdown(self, request): + """Poweroff host.""" + if await self.host_controll.shutdown(): + raise HTTPOk() + raise HTTPMethodNotAllowed() + + async def network_info(self, request): + """Edit network settings.""" + raise HTTPMethodNotAllowed() + + async def network_update(self, request): + """Edit network settings.""" + raise HTTPMethodNotAllowed() + + async def update_host(self, request): + """Update host OS.""" + if await self.host_controll.host_update(): + raise HTTPOk() + raise HTTPMethodNotAllowed() diff --git a/hassio_api/hassio/core.py b/hassio_api/hassio/core.py index c699f8dc8..044274acd 100644 --- a/hassio_api/hassio/core.py +++ b/hassio_api/hassio/core.py @@ -48,8 +48,10 @@ class HassIO(object): host_info = await self.host_controll.info() if host_info: _LOGGER.info( - "Connected to host controll daemon. OS: %s Version: %s", - host_info.get('host'), host_info.get('version')) + "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')) # api views self.api.registerHost(self.host_controll) @@ -61,7 +63,7 @@ class HassIO(object): await self._setup_homeassistant() # start api - self.api.start() + await self.api.start() # run HomeAssistant _LOGGER.info("Run HomeAssistant now.") diff --git a/hassio_api/hassio/dock/homeassistant.py b/hassio_api/hassio/dock/homeassistant.py index 65e10bf53..3ab2d681d 100644 --- a/hassio_api/hassio/dock/homeassistant.py +++ b/hassio_api/hassio/dock/homeassistant.py @@ -4,7 +4,7 @@ import logging import docker from . import DockerBase -from ..tools import get_version_from_env +from ..tools import get_version_from_env, get_local_ip _LOGGER = logging.getLogger(__name__) @@ -31,6 +31,8 @@ class DockerHomeAssistant(DockerBase): if self._is_running(): return + api_endpoint = get_local_ip(self.loop) + try: self.container = self.dock.containers.run( self.image, @@ -42,6 +44,9 @@ class DockerHomeAssistant(DockerBase): "Name": "always", "MaximumRetryCount": 10, }, + environment={ + 'HASSIO': api_endpoint, + } volumes={ self.config.path_config_docker: {'bind': '/config', 'mode': 'rw'}, diff --git a/hassio_api/hassio/host_controll.py b/hassio_api/hassio/host_controll.py index 83dc24dfd..96ee4035d 100644 --- a/hassio_api/hassio/host_controll.py +++ b/hassio_api/hassio/host_controll.py @@ -13,6 +13,11 @@ _LOGGER = logging.getLogger(__name__) TIMEOUT = 15 +LEVEL_POWER = 1 +LEVEL_UPDATE_SUPERVISOR = 2 +LEVEL_UPDATE_HOST = 4 +LEVEL_NETWORK = 8 + class HostControll(object): """Client for host controll.""" diff --git a/hassio_api/hassio/tools.py b/hassio_api/hassio/tools.py index 368510738..827d64d3e 100644 --- a/hassio_api/hassio/tools.py +++ b/hassio_api/hassio/tools.py @@ -3,6 +3,7 @@ import asyncio import logging import json import re +import socket import aiohttp import async_timeout @@ -15,7 +16,10 @@ _RE_VERSION = re.compile(r"VERSION=(.*)") async def fetch_current_versions(websession): - """Fetch current versions from github.""" + """Fetch current versions from github. + + Is a coroutine. + """ try: with async_timeout.timeout(10, loop=websession.loop): async with websession.get(URL_HASSIO_VERSION) as request: @@ -35,3 +39,20 @@ def get_version_from_env(env_list): _LOGGER.error("Can't find VERSION in env") return None + +def get_local_ip(loop): + """Retrieve local IP address. + + Need run inside executor. + """ + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + # Use Google Public DNS server to determine own IP + sock.connect(('8.8.8.8', 80)) + + return sock.getsockname()[0] + except socket.error: + return socket.gethostbyname(socket.gethostname()) + finally: + sock.close() diff --git a/meta-hassio/recipes-support/hassio-host-controll/hassio-host-controll/hassio-hc b/meta-hassio/recipes-support/hassio-host-controll/hassio-host-controll/hassio-hc index 6b63932c2..29e2d9e6f 100644 --- a/meta-hassio/recipes-support/hassio-host-controll/hassio-host-controll/hassio-hc +++ b/meta-hassio/recipes-support/hassio-host-controll/hassio-host-controll/hassio-hc @@ -17,7 +17,7 @@ do IFS=" " read -r -a parse <<< $cmd if [ ${parse[0]} == "info" ]; then - echo "{ \"host\": \"resinos\", \"version\": \"$RESINOS_HASSIO_VERSION\" }" + echo "{ \"level\": 15, \"os\": \"resinos\", \"version\": \"$RESINOS_HASSIO_VERSION\", \"hostname\": \"$CONFIG_HOSTNAME\" }" continue fi if [ ${parse[0]} == "reboot" ]; then