Finish new homeassistant handling

This commit is contained in:
pvizeli 2017-07-11 16:19:24 +02:00
parent 02c8baef68
commit 906c4e03fb
8 changed files with 113 additions and 50 deletions

8
API.md
View File

@ -269,7 +269,9 @@ Optional:
{ {
"version": "INSTALL_VERSION", "version": "INSTALL_VERSION",
"last_version": "LAST_VERSION", "last_version": "LAST_VERSION",
"devices": [] "devices": [""],
"image": "str",
"custom": "bool -> if custom image"
} }
``` ```
@ -291,9 +293,13 @@ Output the raw docker log
```json ```json
{ {
"devices": [], "devices": [],
"image": "Optional|null",
"last_version": "Optional for custom image|null"
} }
``` ```
Image with `null` and last_version with `null` reset this options.
### REST API addons ### REST API addons
- POST `/addons/reload` - POST `/addons/reload`

View File

@ -5,7 +5,8 @@ import logging
import voluptuous as vol import voluptuous as vol
from .util import api_process, api_process_raw, api_validate from .util import api_process, api_process_raw, api_validate
from ..const import ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES, ATTR_IMAGE, ATTR_CUSTOM)
from ..validate import HASS_DEVICES from ..validate import HASS_DEVICES
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -13,6 +14,9 @@ _LOGGER = logging.getLogger(__name__)
SCHEMA_OPTIONS = vol.Schema({ SCHEMA_OPTIONS = vol.Schema({
vol.Optional(ATTR_DEVICES): HASS_DEVICES, vol.Optional(ATTR_DEVICES): HASS_DEVICES,
vol.Inclusive(ATTR_IMAGE, 'custom_hass'): vol.Any(None, vol.Coerce(str)),
vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'):
vol.Any(None, vol.Coerce(str)),
}) })
SCHEMA_VERSION = vol.Schema({ SCHEMA_VERSION = vol.Schema({
@ -34,8 +38,10 @@ class APIHomeAssistant(object):
"""Return host information.""" """Return host information."""
return { return {
ATTR_VERSION: self.homeassistant.version, ATTR_VERSION: self.homeassistant.version,
ATTR_LAST_VERSION: self.config.last_homeassistant, ATTR_LAST_VERSION: self.homeassistant.last_version,
ATTR_DEVICES: self.config.homeassistant_devices, ATTR_IMAGE: self.homeassistant.image,
ATTR_DEVICES: self.homeassistant.devices,
ATTR_CUSTOM: self.homeassistant.is_custom_image,
} }
@api_process @api_process
@ -44,7 +50,11 @@ class APIHomeAssistant(object):
body = await api_validate(SCHEMA_OPTIONS, request) body = await api_validate(SCHEMA_OPTIONS, request)
if ATTR_DEVICES in body: if ATTR_DEVICES in body:
self.config.homeassistant_devices = body[ATTR_DEVICES] self.homeassistant.devices = body[ATTR_DEVICES]
if ATTR_IMAGE in body:
self.homeassistant.set_custom(
body[ATTR_IMAGE], body[ATTR_LAST_VERSION])
return True return True

View File

@ -95,6 +95,7 @@ ATTR_SIZE = 'size'
ATTR_TYPE = 'type' ATTR_TYPE = 'type'
ATTR_TIMEOUT = 'timeout' ATTR_TIMEOUT = 'timeout'
ATTR_AUTO_UPDATE = 'auto_update' ATTR_AUTO_UPDATE = 'auto_update'
ATTR_CUSTOM = 'custom'
STARTUP_INITIALIZE = 'initialize' STARTUP_INITIALIZE = 'initialize'
STARTUP_BEFORE = 'before' STARTUP_BEFORE = 'before'

View File

@ -13,13 +13,12 @@ from .const import (
RUN_UPDATE_SUPERVISOR_TASKS, RUN_WATCHDOG_HOMEASSISTANT, RUN_UPDATE_SUPERVISOR_TASKS, RUN_WATCHDOG_HOMEASSISTANT,
RUN_CLEANUP_API_SESSIONS, STARTUP_AFTER, STARTUP_BEFORE, RUN_CLEANUP_API_SESSIONS, STARTUP_AFTER, STARTUP_BEFORE,
STARTUP_INITIALIZE, RUN_RELOAD_SNAPSHOTS_TASKS, RUN_UPDATE_ADDONS_TASKS) STARTUP_INITIALIZE, RUN_RELOAD_SNAPSHOTS_TASKS, RUN_UPDATE_ADDONS_TASKS)
from .homeassistant import HomeAssistant
from .scheduler import Scheduler from .scheduler import Scheduler
from .dock.homeassistant import DockerHomeAssistant
from .dock.supervisor import DockerSupervisor from .dock.supervisor import DockerSupervisor
from .snapshots import SnapshotsManager from .snapshots import SnapshotsManager
from .tasks import ( from .tasks import (
hassio_update, homeassistant_watchdog, homeassistant_setup, hassio_update, homeassistant_watchdog, api_sessions_cleanup, addons_update)
api_sessions_cleanup, addons_update)
from .tools import get_local_ip, fetch_timezone from .tools import get_local_ip, fetch_timezone
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -41,7 +40,9 @@ class HassIO(object):
# init basic docker container # init basic docker container
self.supervisor = DockerSupervisor(config, loop, self.dock, self.stop) self.supervisor = DockerSupervisor(config, loop, self.dock, self.stop)
self.homeassistant = DockerHomeAssistant(config, loop, self.dock)
# init homeassistant
self.homeassistant = HomeAssistant(config, loop, self.dock)
# init HostControl # init HostControl
self.host_control = HostControl(loop) self.host_control = HostControl(loop)
@ -94,13 +95,8 @@ class HassIO(object):
api_sessions_cleanup(self.config), RUN_CLEANUP_API_SESSIONS, api_sessions_cleanup(self.config), RUN_CLEANUP_API_SESSIONS,
now=True) now=True)
# first start of supervisor? # Load homeassistant
if not await self.homeassistant.exists(): await self.homeassistant.prepare():
_LOGGER.info("No HomeAssistant docker found.")
await homeassistant_setup(
self.config, self.loop, self.homeassistant, self.websession)
else:
await self.homeassistant.attach()
# Load addons # Load addons
await self.addons.prepare() await self.addons.prepare()
@ -132,6 +128,10 @@ class HassIO(object):
loop=self.loop loop=self.loop
) )
# If laningpage / run upgrade in background
if self.homeassistant.version == 'landingpage':
self.loop.create_task(self.homeassistant.install())
# start api # start api
await self.api.start() await self.api.start()
_LOGGER.info("Start hassio api on %s", self.config.api_endpoint) _LOGGER.info("Start hassio api on %s", self.config.api_endpoint)

View File

@ -1,8 +1,10 @@
"""HomeAssistant control object.""" """HomeAssistant control object."""
import asyncio
import logging import logging
import os import os
from .const import FILE_HASSIO_HOMEASSISTANT, ATTR_DEVICES, ATTR_IMAGE from .const import (
FILE_HASSIO_HOMEASSISTANT, ATTR_DEVICES, ATTR_IMAGE, ATTR_LAST_VERSION)
from .dock.homeassistant import DockerHomeAssistant from .dock.homeassistant import DockerHomeAssistant
from .tools import JsonConfig from .tools import JsonConfig
from .validate import SCHEMA_HASS_CONFIG from .validate import SCHEMA_HASS_CONFIG
@ -11,14 +13,24 @@ from .validate import SCHEMA_HASS_CONFIG
class HomeAssistant(JsonConfig): class HomeAssistant(JsonConfig):
"""Hass core object for handle it.""" """Hass core object for handle it."""
def __init__(self, config, loop, dock): def __init__(self, config, loop, dock, websession):
"""Initialize hass object.""" """Initialize hass object."""
super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG) super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG)
self.config = config self.config = config
self.loop = loop self.loop = loop
self.websession = websession
self.docker = DockerHomeAssistant(config, loop, dock, self)
async def prepare(self): async def prepare(self):
"""Prepare HomeAssistant object.""" """Prepare HomeAssistant object."""
if not await self.docker.exists():
_LOGGER.info("No HomeAssistant docker %s found.", self.image)
if self.is_custom_image:
await self.install()
else:
await self.install_landingpage()
else:
await self.docker.attach()
@property @property
def version(self): def version(self):
@ -28,6 +40,8 @@ class HomeAssistant(JsonConfig):
@property @property
def last_version(self): def last_version(self):
"""Return last available version of homeassistant.""" """Return last available version of homeassistant."""
if self.is_custom_image:
return self._data.get(ATTR_LAST_VERSION)
return self.config.last_homeassistant return self.config.last_homeassistant
@property @property
@ -37,14 +51,10 @@ class HomeAssistant(JsonConfig):
return self._data[ATTR_IMAGE] return self._data[ATTR_IMAGE]
return os.environ['HOMEASSISTANT_REPOSITORY'] return os.environ['HOMEASSISTANT_REPOSITORY']
@image.setter @property
def image(self, value): def is_custom_image(self):
"""Set image name of hass containter.""" """Return True if a custom image is used."""
if value is None: return ATTR_IMAGE in self._data
self._data.pop(ATTR_IMAGE, None)
else:
self._data[ATTR_IMAGE] = value
self.save()
@property @property
def devices(self): def devices(self):
@ -56,3 +66,57 @@ class HomeAssistant(JsonConfig):
"""Set extend device mapping.""" """Set extend device mapping."""
self._data[ATTR_DEVICES] = value self._data[ATTR_DEVICES] = value
self.save() self.save()
def set_custom(self, image, version):
"""Set a custom image for homeassistant."""
# reset
if image is None and version is None:
self._data.pop(ATTR_IMAGE, None)
self._data.pop(ATTR_VERSION, None)
else:
if image:
self._data[ATTR_IMAGE] = image
if version:
self._data[ATTR_VERSION] = version
self.save()
async def install_landingpage(self):
"""Install a landingpage."""
_LOGGER.info("Setup HomeAssistant landingpage")
while True:
if self.docker.install('landingpage'):
break
_LOGGER.warning("Fails install landingpage, retry after 60sec")
await asyncio.sleep(60, loop=self.loop)
async def install(self):
"""Install a landingpage."""
_LOGGER.info("Setup HomeAssistant")
while True:
# read homeassistant tag and install it
if not self.last_version:
await self.config.fetch_update_infos(websession)
tag = self.last_version
if tag and await self.docker.install(tag):
break
_LOGGER.warning("Error on install HomeAssistant. Retry in 60sec")
await asyncio.sleep(60, loop=loop)
# store version
_LOGGER.info("HomeAssistant docker now installed")
async def update(self, version=None):
"""Update HomeAssistant version."""
version = version or self.last_version
if version == self.version:
return True
return self.docker.update(version)
def run(self):
"""Run HomeAssistant docker.
Return a coroutine.
"""
return self.docker.run()

View File

@ -47,7 +47,7 @@ class SnapshotsManager(object):
# set general data # set general data
snapshot.homeassistant_version = self.homeassistant.version snapshot.homeassistant_version = self.homeassistant.version
snapshot.homeassistant_devices = self.config.homeassistant_devices snapshot.homeassistant_devices = self.homeassistant.devices
snapshot.repositories = self.config.addons_repositories snapshot.repositories = self.config.addons_repositories
return snapshot return snapshot
@ -198,8 +198,7 @@ class SnapshotsManager(object):
await snapshot.restore_folders() await snapshot.restore_folders()
# start homeassistant restore # start homeassistant restore
self.config.homeassistant_devices = \ self.homeassistant.devices = snapshot.homeassistant_devices
snapshot.homeassistant_devices
task_hass = self.loop.create_task( task_hass = self.loop.create_task(
self.homeassistant.update(snapshot.homeassistant_version)) self.homeassistant.update(snapshot.homeassistant_version))
@ -281,8 +280,7 @@ class SnapshotsManager(object):
await snapshot.restore_folders(folders) await snapshot.restore_folders(folders)
if homeassistant: if homeassistant:
self.config.homeassistant_devices = \ self.homeassistant.devices = snapshot.homeassistant_devices
snapshot.homeassistant_devices
tasks.append(self.homeassistant.update( tasks.append(self.homeassistant.update(
snapshot.homeassistant_version)) snapshot.homeassistant_version))

View File

@ -66,20 +66,3 @@ def homeassistant_watchdog(loop, homeassistant):
loop.create_task(homeassistant.run()) loop.create_task(homeassistant.run())
return _homeassistant_watchdog return _homeassistant_watchdog
async def homeassistant_setup(config, loop, homeassistant, websession):
"""Install a homeassistant docker container."""
while True:
# read homeassistant tag and install it
if not config.last_homeassistant:
await config.fetch_update_infos(websession)
tag = config.last_homeassistant
if tag and await homeassistant.install(tag):
break
_LOGGER.warning("Error on setup HomeAssistant. Retry in 60.")
await asyncio.sleep(60, loop=loop)
# store version
_LOGGER.info("HomeAssistant docker now installed.")

View File

@ -1,7 +1,7 @@
"""Validate functions.""" """Validate functions."""
import voluptuous as vol import voluptuous as vol
from .const import ATTR_DEVICES, ATTR_IMAGE from .const import ATTR_DEVICES, ATTR_IMAGE, ATTR_LAST_VERSION
NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535)) NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
@ -37,5 +37,6 @@ DOCKER_PORTS = vol.Schema({
SCHEMA_HASS_CONFIG = vol.Schema({ SCHEMA_HASS_CONFIG = vol.Schema({
vol.Optional(ATTR_DEVICES, default=[]): HASS_DEVICES, vol.Optional(ATTR_DEVICES, default=[]): HASS_DEVICES,
vol.Optional(ATTR_IMAGE): vol.Coerce(str) vol.inclusive(ATTR_IMAGE, 'custom_hass'): vol.Coerce(str),
vol.inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Coerce(str),
}) })