Add support for home-assistant bootup (#349)

* Add support for home-assistant bootup

* fix bug

* fix

* fix ip bug

* bugfix
This commit is contained in:
Pascal Vizeli 2018-02-09 01:27:45 +01:00 committed by GitHub
parent b50756785e
commit dec04386bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 101 additions and 52 deletions

6
API.md
View File

@ -268,7 +268,8 @@ Optional:
"boot": "bool",
"port": 8123,
"ssl": "bool",
"watchdog": "bool"
"watchdog": "bool",
"startup_time": 600
}
```
@ -300,7 +301,8 @@ Output is the raw Docker log.
"port": "port for access hass",
"ssl": "bool",
"password": "",
"watchdog": "bool"
"watchdog": "bool",
"startup_time": 600
}
```

View File

@ -581,12 +581,12 @@ class Addon(CoreSysAttributes):
return STATE_STOPPED
@check_installed
def start(self):
"""Set options and start addon.
async def start(self):
"""Set options and start addon."""
if not self.write_options():
return False
Return a coroutine.
"""
return self.instance.run()
return await self.instance.run()
@check_installed
def stop(self):
@ -611,16 +611,14 @@ class Addon(CoreSysAttributes):
# restore state
if last_state == STATE_STARTED:
await self.instance.run()
await self.start()
return True
@check_installed
def restart(self):
"""Restart addon.
Return a coroutine.
"""
return self.instance.restart()
async def restart(self):
"""Restart addon."""
await self.stop()
return await self.start()
@check_installed
def logs(self):
@ -656,7 +654,7 @@ class Addon(CoreSysAttributes):
# restore state
if last_state == STATE_STARTED:
await self.instance.run()
await self.start()
return True
@check_installed

View File

@ -9,7 +9,7 @@ from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_IMAGE, ATTR_CUSTOM, ATTR_BOOT,
ATTR_PORT, ATTR_PASSWORD, ATTR_SSL, ATTR_WATCHDOG, ATTR_CPU_PERCENT,
ATTR_MEMORY_USAGE, ATTR_MEMORY_LIMIT, ATTR_NETWORK_RX, ATTR_NETWORK_TX,
ATTR_BLK_READ, ATTR_BLK_WRITE, CONTENT_TYPE_BINARY)
ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_STARTUP_TIME, CONTENT_TYPE_BINARY)
from ..coresys import CoreSysAttributes
from ..validate import NETWORK_PORT, DOCKER_IMAGE
@ -27,6 +27,8 @@ SCHEMA_OPTIONS = vol.Schema({
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)),
vol.Optional(ATTR_SSL): vol.Boolean(),
vol.Optional(ATTR_WATCHDOG): vol.Boolean(),
vol.Optional(ATTR_STARTUP_TIME):
vol.All(vol.Coerce(int), vol.Range(min=60)),
})
SCHEMA_VERSION = vol.Schema({
@ -49,6 +51,7 @@ class APIHomeAssistant(CoreSysAttributes):
ATTR_PORT: self._homeassistant.api_port,
ATTR_SSL: self._homeassistant.api_ssl,
ATTR_WATCHDOG: self._homeassistant.watchdog,
ATTR_STARTUP_TIME: self._homeassistant.startup_time,
}
@api_process
@ -75,6 +78,9 @@ class APIHomeAssistant(CoreSysAttributes):
if ATTR_WATCHDOG in body:
self._homeassistant.watchdog = body[ATTR_WATCHDOG]
if ATTR_STARTUP_TIME in body:
self._homeassistant.startup_time = body[ATTR_STARTUP_TIME]
self._homeassistant.save_data()
return True
@ -115,7 +121,7 @@ class APIHomeAssistant(CoreSysAttributes):
@api_process
def start(self, request):
"""Start homeassistant."""
return asyncio.shield(self._homeassistant.run(), loop=self._loop)
return asyncio.shield(self._homeassistant.start(), loop=self._loop)
@api_process
def restart(self, request):

View File

@ -155,6 +155,7 @@ ATTR_CONFIG = 'config'
ATTR_DISCOVERY_ID = 'discovery_id'
ATTR_SERVICES = 'services'
ATTR_DISCOVERY = 'discovery'
ATTR_STARTUP_TIME = 'startup_time'
SERVICE_MQTT = 'mqtt'

View File

@ -84,7 +84,7 @@ class HassIO(CoreSysAttributes):
# run HomeAssistant
if self._homeassistant.boot:
await self._homeassistant.run()
await self._homeassistant.start()
# start addon mark as application
await self._addons.auto_boot(STARTUP_APPLICATION)

View File

@ -225,10 +225,6 @@ class DockerAddon(DockerInterface):
# cleanup
self._stop()
# write config
if not self.addon.write_options():
return False
ret = self._docker.run(
self.image,
name=self.name,
@ -337,15 +333,6 @@ class DockerAddon(DockerInterface):
self._cleanup()
return True
def _restart(self):
"""Restart docker container.
Addons prepare some thing on start and that is normaly not repeatable.
Need run inside executor.
"""
self._stop()
return self._run()
@docker_process
def write_stdin(self, data):
"""Write to add-on stdin."""

View File

@ -3,6 +3,8 @@ import asyncio
import logging
import os
import re
import socket
import time
import aiohttp
from aiohttp.hdrs import CONTENT_TYPE
@ -10,7 +12,7 @@ from aiohttp.hdrs import CONTENT_TYPE
from .const import (
FILE_HASSIO_HOMEASSISTANT, ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_UUID,
ATTR_BOOT, ATTR_PASSWORD, ATTR_PORT, ATTR_SSL, ATTR_WATCHDOG,
HEADER_HA_ACCESS, CONTENT_TYPE_JSON)
ATTR_STARTUP_TIME, HEADER_HA_ACCESS, CONTENT_TYPE_JSON)
from .coresys import CoreSysAttributes
from .docker.homeassistant import DockerHomeAssistant
from .utils import convert_to_ascii
@ -91,6 +93,16 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
"""Return True if the watchdog should protect Home-Assistant."""
self._data[ATTR_WATCHDOG] = value
@property
def startup_time(self):
"""Return time to wait for Home-Assistant startup."""
return self._data[ATTR_STARTUP_TIME]
@startup_time.setter
def startup_time(self, value):
"""Set time to wait for Home-Assistant startup."""
self._data[ATTR_STARTUP_TIME] = value
@property
def version(self):
"""Return version of running homeassistant."""
@ -156,8 +168,8 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
_LOGGER.warning("Fails install landingpage, retry after 60sec")
await asyncio.sleep(60, loop=self._loop)
# run landingpage after installation
await self.instance.run()
# Run landingpage after installation
await self.start()
async def install(self):
"""Install a landingpage."""
@ -176,7 +188,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
# finishing
_LOGGER.info("HomeAssistant docker now installed")
if self.boot:
await self.instance.run()
await self.start()
await self.instance.cleanup()
async def update(self, version=None):
@ -193,14 +205,14 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
return await self.instance.update(version)
finally:
if running:
await self.instance.run()
await self.start()
def run(self):
"""Run HomeAssistant docker.
async def start(self):
"""Run HomeAssistant docker."""
if not await self.instance.run():
return False
Return a coroutine.
"""
return self.instance.run()
return await self._block_till_run()
def stop(self):
"""Stop HomeAssistant docker.
@ -209,12 +221,12 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
"""
return self.instance.stop()
def restart(self):
"""Restart HomeAssistant docker.
async def restart(self):
"""Restart HomeAssistant docker."""
if not await self.instance.restart():
return False
Return a coroutine.
"""
return self.instance.restart()
return await self._block_till_run()
def logs(self):
"""Get HomeAssistant docker logs.
@ -310,3 +322,29 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
_LOGGER.warning("Home-Assistant event %s fails", event_type)
return False
return True
async def _block_till_run(self):
"""Block until Home-Assistant is booting up or startup timeout."""
start_time = time.monotonic()
def check_port():
"""Check if port is mapped."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
result = sock.connect_ex((str(self.api_ip), self.api_port))
sock.close()
if result == 0:
return True
return False
except OSError:
pass
while time.monotonic() - start_time < self.startup_time:
if await self._loop.run_in_executor(None, check_port):
_LOGGER.info("Detect a running Home-Assistant instance")
return True
await asyncio.sleep(10, loop=self._loop)
_LOGGER.warning("Don't wait anymore of Home-Assistant startup!")
return False

View File

@ -249,7 +249,7 @@ class SnapshotManager(CoreSysAttributes):
_LOGGER.info("Full-Restore %s wait until homeassistant ready",
snapshot.slug)
await task_hass
await self._homeassistant.run()
await self._homeassistant.start()
except (OSError, ValueError, tarfile.TarError) as err:
_LOGGER.info("Full-Restore %s error: %s", snapshot.slug, err)
@ -310,7 +310,7 @@ class SnapshotManager(CoreSysAttributes):
await asyncio.wait(tasks, loop=self._loop)
# make sure homeassistant run agen
await self._homeassistant.run()
await self._homeassistant.start()
except (OSError, ValueError, tarfile.TarError) as err:
_LOGGER.info("Partial-Restore %s error: %s", snapshot.slug, err)

View File

@ -15,7 +15,7 @@ from ..const import (
ATTR_SLUG, ATTR_NAME, ATTR_DATE, ATTR_ADDONS, ATTR_REPOSITORIES,
ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_VERSION, ATTR_TYPE, ATTR_IMAGE,
ATTR_PORT, ATTR_SSL, ATTR_PASSWORD, ATTR_WATCHDOG, ATTR_BOOT,
ATTR_LAST_VERSION)
ATTR_LAST_VERSION, ATTR_STARTUP_TIME)
from ..coresys import CoreSysAttributes
from ..utils.json import write_json_file
@ -142,6 +142,16 @@ class Snapshot(CoreSysAttributes):
"""Set snapshot homeassistant watchdog options."""
self._data[ATTR_HOMEASSISTANT][ATTR_WATCHDOG] = value
@property
def homeassistant_startup_time(self):
"""Return snapshot homeassistant startup time options."""
return self._data[ATTR_HOMEASSISTANT].get(ATTR_STARTUP_TIME)
@homeassistant_startup_time.setter
def homeassistant_startup_time(self, value):
"""Set snapshot homeassistant startup time options."""
self._data[ATTR_HOMEASSISTANT][ATTR_STARTUP_TIME] = value
@property
def homeassistant_boot(self):
"""Return snapshot homeassistant boot options."""
@ -339,6 +349,7 @@ class Snapshot(CoreSysAttributes):
self.homeassistant_version = self._homeassistant.version
self.homeassistant_watchdog = self._homeassistant.watchdog
self.homeassistant_boot = self._homeassistant.boot
self.homeassistant_startup_time = self._homeassistant.startup_time
# custom image
if self._homeassistant.is_custom_image:
@ -354,6 +365,7 @@ class Snapshot(CoreSysAttributes):
"""Write all data to homeassistant object."""
self._homeassistant.watchdog = self.homeassistant_watchdog
self._homeassistant.boot = self.homeassistant_boot
self._homeassistant.startup_time = self.homeassistant_startup_time
# custom image
if self.homeassistant_image:

View File

@ -6,7 +6,7 @@ from ..const import (
ATTR_REPOSITORIES, ATTR_ADDONS, ATTR_NAME, ATTR_SLUG, ATTR_DATE,
ATTR_VERSION, ATTR_HOMEASSISTANT, ATTR_FOLDERS, ATTR_TYPE, ATTR_IMAGE,
ATTR_PASSWORD, ATTR_PORT, ATTR_SSL, ATTR_WATCHDOG, ATTR_BOOT,
ATTR_LAST_VERSION,
ATTR_LAST_VERSION, ATTR_STARTUP_TIME,
FOLDER_SHARE, FOLDER_HOMEASSISTANT, FOLDER_ADDONS, FOLDER_SSL,
SNAPSHOT_FULL, SNAPSHOT_PARTIAL)
from ..validate import NETWORK_PORT, REPOSITORIES, DOCKER_IMAGE
@ -38,6 +38,8 @@ SCHEMA_SNAPSHOT = vol.Schema({
vol.Optional(ATTR_PORT, default=8123): NETWORK_PORT,
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)),
vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(),
vol.Optional(ATTR_STARTUP_TIME, default=600):
vol.All(vol.Coerce(int), vol.Range(min=60)),
}, extra=vol.REMOVE_EXTRA),
vol.Optional(ATTR_FOLDERS, default=list):
vol.All([vol.In(ALL_FOLDERS)], vol.Unique()),

View File

@ -98,7 +98,7 @@ class Tasks(CoreSysAttributes):
return
_LOGGER.warning("Watchdog found a problem with Home-Assistant docker!")
await self._homeassistant.run()
await self._homeassistant.start()
async def _watchdog_homeassistant_api(self):
"""Create scheduler task for montoring running state of API.

View File

@ -8,7 +8,8 @@ from .const import (
ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL, ATTR_TIMEZONE,
ATTR_ADDONS_CUSTOM_LIST, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT,
ATTR_PASSWORD, ATTR_HOMEASSISTANT, ATTR_HASSIO, ATTR_BOOT, ATTR_LAST_BOOT,
ATTR_SSL, ATTR_PORT, ATTR_WATCHDOG, ATTR_WAIT_BOOT, ATTR_UUID)
ATTR_SSL, ATTR_PORT, ATTR_WATCHDOG, ATTR_WAIT_BOOT, ATTR_UUID,
ATTR_STARTUP_TIME)
NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
@ -72,6 +73,8 @@ SCHEMA_HASS_CONFIG = vol.Schema({
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)),
vol.Optional(ATTR_SSL, default=False): vol.Boolean(),
vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(),
vol.Optional(ATTR_STARTUP_TIME, default=600):
vol.All(vol.Coerce(int), vol.Range(min=60)),
}, extra=vol.REMOVE_EXTRA)