Part 2 of rest api

This commit is contained in:
pvizeli 2017-03-30 16:01:52 +02:00
parent 3f3d013db0
commit 1d627961f5
8 changed files with 169 additions and 12 deletions

View File

@ -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`

View File

@ -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()

View File

@ -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()

View File

@ -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.")

View File

@ -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'},

View File

@ -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."""

View File

@ -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()

View File

@ -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