mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-09-21 02:49:36 +00:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b5b68c5c42 | ||
![]() |
d58e847978 | ||
![]() |
aad9ae6997 | ||
![]() |
139cf4fae4 | ||
![]() |
e01b2da223 | ||
![]() |
cbbe2d2d3c | ||
![]() |
7ca11a96b9 | ||
![]() |
3443d6d715 | ||
![]() |
99730734a0 | ||
![]() |
20fcd28dbe | ||
![]() |
76cead72e8 | ||
![]() |
a0f17ffd1d | ||
![]() |
86d92bdfa2 | ||
![]() |
25a0bc6549 | ||
![]() |
96971e7054 | ||
![]() |
2729877fbf | ||
![]() |
3b8b44fcb7 | ||
![]() |
22e3d50203 | ||
![]() |
2090b336e4 | ||
![]() |
a028f11a4a | ||
![]() |
4edd02ca8e | ||
![]() |
85dad8c077 | ||
![]() |
fff2ac3c47 | ||
![]() |
f99c6335c0 | ||
![]() |
98f0ff2a01 | ||
![]() |
43c5fcd159 | ||
![]() |
784b57622c | ||
![]() |
d8d17998fc |
55
API.md
55
API.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## HassIO REST API
|
## HassIO REST API
|
||||||
|
|
||||||
Interface for HomeAssistant to controll things from supervisor.
|
Interface for HomeAssistant to control things from supervisor.
|
||||||
|
|
||||||
On error:
|
On error:
|
||||||
```json
|
```json
|
||||||
@@ -29,14 +29,15 @@ On success
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": "INSTALL_VERSION",
|
"version": "INSTALL_VERSION",
|
||||||
"current": "CURRENT_VERSION",
|
"last_version": "LAST_VERSION",
|
||||||
"beta": "true|false",
|
"beta_channel": "true|false",
|
||||||
"addons": [
|
"addons": [
|
||||||
{
|
{
|
||||||
"name": "xy bla",
|
"name": "xy bla",
|
||||||
"slug": "xy",
|
"slug": "xy",
|
||||||
"version": "CURRENT_VERSION",
|
"version": "LAST_VERSION",
|
||||||
"installed": "none|INSTALL_VERSION",
|
"installed": "none|INSTALL_VERSION",
|
||||||
|
"dedicated": "bool",
|
||||||
"description": "description"
|
"description": "description"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -54,7 +55,7 @@ Optional:
|
|||||||
- `/supervisor/option`
|
- `/supervisor/option`
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"beta": "true|false"
|
"beta_channel": "true|false"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -62,6 +63,10 @@ Optional:
|
|||||||
|
|
||||||
Reload addons/version.
|
Reload addons/version.
|
||||||
|
|
||||||
|
- `/supervisor/logs`
|
||||||
|
|
||||||
|
Output the raw docker log
|
||||||
|
|
||||||
### Host
|
### Host
|
||||||
|
|
||||||
- `/host/shutdown`
|
- `/host/shutdown`
|
||||||
@@ -69,14 +74,15 @@ Reload addons/version.
|
|||||||
- `/host/reboot`
|
- `/host/reboot`
|
||||||
|
|
||||||
- `/host/info`
|
- `/host/info`
|
||||||
See HostControll info command.
|
See HostControl info command.
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"os": "",
|
"type": "",
|
||||||
"version": "",
|
"version": "",
|
||||||
"current": "",
|
"last_version": "",
|
||||||
"level": "",
|
"features": ["shutdown", "reboot", "update", "network_info", "network_control"],
|
||||||
"hostname": "",
|
"hostname": "",
|
||||||
|
"os": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -111,7 +117,7 @@ Optional:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": "INSTALL_VERSION",
|
"version": "INSTALL_VERSION",
|
||||||
"current": "CURRENT_VERSION"
|
"last_version": "LAST_VERSION"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -123,13 +129,17 @@ Optional:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- `/homeassistant/logs`
|
||||||
|
|
||||||
|
Output the raw docker log
|
||||||
|
|
||||||
### REST API addons
|
### REST API addons
|
||||||
|
|
||||||
- `/addons/{addon}/info`
|
- `/addons/{addon}/info`
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": "VERSION",
|
"version": "VERSION",
|
||||||
"current": "CURRENT_VERSION",
|
"last_version": "LAST_VERSION",
|
||||||
"state": "started|stopped",
|
"state": "started|stopped",
|
||||||
"boot": "auto|manual",
|
"boot": "auto|manual",
|
||||||
"options": {},
|
"options": {},
|
||||||
@@ -138,7 +148,10 @@ Optional:
|
|||||||
|
|
||||||
- `/addons/{addon}/options`
|
- `/addons/{addon}/options`
|
||||||
```json
|
```json
|
||||||
{ }
|
{
|
||||||
|
"boot": "auto|manual",
|
||||||
|
"options": {},
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `/addons/{addon}/start`
|
- `/addons/{addon}/start`
|
||||||
@@ -163,14 +176,18 @@ Optional:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Host Controll
|
- `/addons/{addon}/logs`
|
||||||
|
|
||||||
|
Output the raw docker log
|
||||||
|
|
||||||
|
## Host Control
|
||||||
|
|
||||||
Communicate over unix socket with a host daemon.
|
Communicate over unix socket with a host daemon.
|
||||||
|
|
||||||
- commands
|
- commands
|
||||||
```
|
```
|
||||||
# info
|
# info
|
||||||
-> {'os', 'version', 'current', 'level', 'hostname'}
|
-> {'type', 'version', 'last_version', 'features', 'hostname'}
|
||||||
# reboot
|
# reboot
|
||||||
# shutdown
|
# shutdown
|
||||||
# host-update [v]
|
# host-update [v]
|
||||||
@@ -184,10 +201,12 @@ Communicate over unix socket with a host daemon.
|
|||||||
# network int route xy
|
# network int route xy
|
||||||
```
|
```
|
||||||
|
|
||||||
level:
|
features:
|
||||||
- 1: power functions
|
- shutdown
|
||||||
- 2: host update
|
- reboot
|
||||||
- 4: network functions
|
- update
|
||||||
|
- network_info
|
||||||
|
- network_control
|
||||||
|
|
||||||
Answer:
|
Answer:
|
||||||
```
|
```
|
||||||
|
14
README.md
14
README.md
@@ -1,13 +1,14 @@
|
|||||||
# HassIO
|
# HassIO
|
||||||
First private cloud solution for home automation.
|
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)
|
[HassIO-Addons](https://github.com/pvizeli/hassio-addons) | [HassIO-Build](https://github.com/pvizeli/hassio-build)
|
||||||
|
|
||||||
|
**HassIO is at the moment on development and not ready to use productive!**
|
||||||
|
|
||||||
## Feature in progress
|
## Feature in progress
|
||||||
- Backup/Restore
|
- Backup/Restore
|
||||||
- Read docker logs and extend to api
|
|
||||||
- MQTT addon
|
- MQTT addon
|
||||||
- DHCP-Server addon
|
- DHCP-Server addon
|
||||||
|
|
||||||
@@ -32,8 +33,8 @@ After extracting the archive, flash it to a drive using [Etcher](https://etcher.
|
|||||||
## History
|
## History
|
||||||
- **0.1**: First techpreview with dumy supervisor (ResinOS 2.0.0-RC5)
|
- **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.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.3**: Update HostControl 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.4**: Update HostControl and bring resinos OTA (resinhub) back (ResinOS 2.0.0-rev3)
|
||||||
|
|
||||||
## Configuring the image
|
## 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).
|
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).
|
||||||
@@ -48,3 +49,8 @@ Read logoutput from supervisor:
|
|||||||
journalctl -f -u resin-supervisor.service
|
journalctl -f -u resin-supervisor.service
|
||||||
docker logs homeassistant
|
docker logs homeassistant
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Install on a own System
|
||||||
|
|
||||||
|
We have a installer to install HassIO on own linux device without our hardware image:
|
||||||
|
https://github.com/pvizeli/hassio-build/tree/master/install
|
||||||
|
@@ -36,6 +36,7 @@ class AddonManager(AddonsData):
|
|||||||
for addon in self.list_installed:
|
for addon in self.list_installed:
|
||||||
self.dockers[addon] = DockerAddon(
|
self.dockers[addon] = DockerAddon(
|
||||||
self.config, self.loop, self.dock, self, addon)
|
self.config, self.loop, self.dock, self, addon)
|
||||||
|
await self.dockers[addon].attach()
|
||||||
|
|
||||||
async def reload(self):
|
async def reload(self):
|
||||||
"""Update addons from repo and reload list."""
|
"""Update addons from repo and reload list."""
|
||||||
@@ -44,13 +45,8 @@ class AddonManager(AddonsData):
|
|||||||
self.read_addons_repo()
|
self.read_addons_repo()
|
||||||
|
|
||||||
# remove stalled addons
|
# remove stalled addons
|
||||||
tasks = []
|
|
||||||
for addon in self.list_removed:
|
for addon in self.list_removed:
|
||||||
_LOGGER.info("Old addon %s found")
|
_LOGGER.warning("Dedicated addon '%s' found!", addon)
|
||||||
tasks.append(self.loop.create_task(self.uninstall(addon)))
|
|
||||||
|
|
||||||
if tasks:
|
|
||||||
await asyncio.wait(tasks, loop=self.loop)
|
|
||||||
|
|
||||||
async def auto_boot(self, start_type):
|
async def auto_boot(self, start_type):
|
||||||
"""Boot addons with mode auto."""
|
"""Boot addons with mode auto."""
|
||||||
@@ -82,12 +78,12 @@ class AddonManager(AddonsData):
|
|||||||
addon_docker = DockerAddon(
|
addon_docker = DockerAddon(
|
||||||
self.config, self.loop, self.dock, self, addon)
|
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):
|
if not await addon_docker.install(version):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.dockers[addon] = addon_docker
|
self.dockers[addon] = addon_docker
|
||||||
self.set_install_addon(addon, version)
|
self.set_addon_install(addon, version)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def uninstall(self, addon):
|
async def uninstall(self, addon):
|
||||||
@@ -109,7 +105,7 @@ class AddonManager(AddonsData):
|
|||||||
shutil.rmtree(self.path_data(addon))
|
shutil.rmtree(self.path_data(addon))
|
||||||
|
|
||||||
self.dockers.pop(addon)
|
self.dockers.pop(addon)
|
||||||
self.set_uninstall_addon(addon)
|
self.set_addon_uninstall(addon)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def state(self, addon):
|
async def state(self, addon):
|
||||||
@@ -144,16 +140,25 @@ class AddonManager(AddonsData):
|
|||||||
|
|
||||||
async def update(self, addon, version=None):
|
async def update(self, addon, version=None):
|
||||||
"""Update addon."""
|
"""Update addon."""
|
||||||
if not self.is_installed(addon):
|
|
||||||
_LOGGER.error("Addon %s is not installed", addon)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if addon not in self.dockers:
|
if addon not in self.dockers:
|
||||||
_LOGGER.error("No docker found for addon %s", addon)
|
_LOGGER.error("No docker found for addon %s", addon)
|
||||||
return False
|
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
|
||||||
if await self.dockers[addon].update(version):
|
if await self.dockers[addon].update(version):
|
||||||
self.set_version(addon, version)
|
self.set_addon_update(addon, version)
|
||||||
|
if is_running:
|
||||||
|
await self.start(addon)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def logs(self, addon):
|
||||||
|
"""Return addons log output."""
|
||||||
|
if addon not in self.dockers:
|
||||||
|
_LOGGER.error("No docker found for addon %s", addon)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return await self.dockers[addon].logs()
|
||||||
|
@@ -10,13 +10,15 @@ from ..const import (
|
|||||||
FILE_HASSIO_ADDONS, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON,
|
FILE_HASSIO_ADDONS, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON,
|
||||||
ATTR_STARTUP, ATTR_BOOT, ATTR_MAP_SSL, ATTR_MAP_CONFIG, ATTR_OPTIONS,
|
ATTR_STARTUP, ATTR_BOOT, ATTR_MAP_SSL, ATTR_MAP_CONFIG, ATTR_OPTIONS,
|
||||||
ATTR_PORTS, BOOT_AUTO, DOCKER_REPO, ATTR_INSTALLED, ATTR_SCHEMA,
|
ATTR_PORTS, BOOT_AUTO, DOCKER_REPO, ATTR_INSTALLED, ATTR_SCHEMA,
|
||||||
ATTR_IMAGE)
|
ATTR_IMAGE, ATTR_DEDICATED)
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
from ..tools import read_json_file, write_json_file
|
from ..tools import read_json_file, write_json_file
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ADDONS_REPO_PATTERN = "{}/*/config.json"
|
ADDONS_REPO_PATTERN = "{}/*/config.json"
|
||||||
|
SYSTEM = "system"
|
||||||
|
USER = "user"
|
||||||
|
|
||||||
|
|
||||||
class AddonsData(Config):
|
class AddonsData(Config):
|
||||||
@@ -26,12 +28,22 @@ class AddonsData(Config):
|
|||||||
"""Initialize data holder."""
|
"""Initialize data holder."""
|
||||||
super().__init__(FILE_HASSIO_ADDONS)
|
super().__init__(FILE_HASSIO_ADDONS)
|
||||||
self.config = config
|
self.config = config
|
||||||
self._addons_data = {}
|
self._system_data = self._data.get(SYSTEM, {})
|
||||||
|
self._user_data = self._data.get(USER, {})
|
||||||
|
self._current_data = {}
|
||||||
self.arch = None
|
self.arch = None
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""Store data to config file."""
|
||||||
|
self._data = {
|
||||||
|
USER: self._user_data,
|
||||||
|
SYSTEM: self._system_data,
|
||||||
|
}
|
||||||
|
super().save()
|
||||||
|
|
||||||
def read_addons_repo(self):
|
def read_addons_repo(self):
|
||||||
"""Read data from addons repository."""
|
"""Read data from addons repository."""
|
||||||
self._addons_data = {}
|
self._current_data = {}
|
||||||
|
|
||||||
self._read_addons_folder(self.config.path_addons_repo)
|
self._read_addons_folder(self.config.path_addons_repo)
|
||||||
self._read_addons_folder(self.config.path_addons_custom)
|
self._read_addons_folder(self.config.path_addons_custom)
|
||||||
@@ -45,7 +57,7 @@ class AddonsData(Config):
|
|||||||
addon_config = read_json_file(addon)
|
addon_config = read_json_file(addon)
|
||||||
|
|
||||||
addon_config = SCHEMA_ADDON_CONFIG(addon_config)
|
addon_config = SCHEMA_ADDON_CONFIG(addon_config)
|
||||||
self._addons_data[addon_config[ATTR_SLUG]] = addon_config
|
self._current_data[addon_config[ATTR_SLUG]] = addon_config
|
||||||
|
|
||||||
except (OSError, KeyError):
|
except (OSError, KeyError):
|
||||||
_LOGGER.warning("Can't read %s", addon)
|
_LOGGER.warning("Can't read %s", addon)
|
||||||
@@ -57,24 +69,25 @@ class AddonsData(Config):
|
|||||||
@property
|
@property
|
||||||
def list_installed(self):
|
def list_installed(self):
|
||||||
"""Return a list of installed addons."""
|
"""Return a list of installed addons."""
|
||||||
return set(self._data.keys())
|
return set(self._system_data.keys())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def list_all(self):
|
def list_api(self):
|
||||||
"""Return a list of available addons."""
|
"""Return a list of available addons for api."""
|
||||||
return set(self._addons_data.keys())
|
|
||||||
|
|
||||||
@property
|
|
||||||
def list(self):
|
|
||||||
"""Return a list of available addons."""
|
|
||||||
data = []
|
data = []
|
||||||
for addon, values in self._addons_data.items():
|
all_addons = {**self._system_data, **self._current_data}
|
||||||
|
dedicated = self.list_removed
|
||||||
|
|
||||||
|
for addon, values in all_addons.items():
|
||||||
|
i_version = self._user_data.get(addon, {}).get(ATTR_VERSION)
|
||||||
|
|
||||||
data.append({
|
data.append({
|
||||||
ATTR_NAME: values[ATTR_NAME],
|
ATTR_NAME: values[ATTR_NAME],
|
||||||
ATTR_SLUG: values[ATTR_SLUG],
|
ATTR_SLUG: values[ATTR_SLUG],
|
||||||
ATTR_DESCRIPTON: values[ATTR_DESCRIPTON],
|
ATTR_DESCRIPTON: values[ATTR_DESCRIPTON],
|
||||||
ATTR_VERSION: values[ATTR_VERSION],
|
ATTR_VERSION: values[ATTR_VERSION],
|
||||||
ATTR_INSTALLED: self._data.get(addon, {}).get(ATTR_VERSION),
|
ATTR_INSTALLED: i_version,
|
||||||
|
ATTR_DEDICATED: addon in dedicated,
|
||||||
})
|
})
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@@ -82,12 +95,12 @@ class AddonsData(Config):
|
|||||||
def list_startup(self, start_type):
|
def list_startup(self, start_type):
|
||||||
"""Get list of installed addon with need start by type."""
|
"""Get list of installed addon with need start by type."""
|
||||||
addon_list = set()
|
addon_list = set()
|
||||||
for addon in self._data.keys():
|
for addon in self._system_data.keys():
|
||||||
if self.get_boot(addon) != BOOT_AUTO:
|
if self.get_boot(addon) != BOOT_AUTO:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self._addons_data[addon][ATTR_STARTUP] == start_type:
|
if self._system_data[addon][ATTR_STARTUP] == start_type:
|
||||||
addon_list.add(addon)
|
addon_list.add(addon)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
_LOGGER.warning("Orphaned addon detect %s", addon)
|
_LOGGER.warning("Orphaned addon detect %s", addon)
|
||||||
@@ -99,106 +112,111 @@ class AddonsData(Config):
|
|||||||
def list_removed(self):
|
def list_removed(self):
|
||||||
"""Return local addons they not support from repo."""
|
"""Return local addons they not support from repo."""
|
||||||
addon_list = set()
|
addon_list = set()
|
||||||
for addon in self._data.keys():
|
for addon in self._system_data.keys():
|
||||||
if addon not in self._addons_data:
|
if addon not in self._current_data:
|
||||||
addon_list.add(addon)
|
addon_list.add(addon)
|
||||||
|
|
||||||
return addon_list
|
return addon_list
|
||||||
|
|
||||||
def exists_addon(self, addon):
|
def exists_addon(self, addon):
|
||||||
"""Return True if a addon exists."""
|
"""Return True if a addon exists."""
|
||||||
return addon in self._addons_data
|
return addon in self._current_data or addon in self._system_data
|
||||||
|
|
||||||
def is_installed(self, addon):
|
def is_installed(self, addon):
|
||||||
"""Return True if a addon is installed."""
|
"""Return True if a addon is installed."""
|
||||||
return addon in self._data
|
return addon in self._system_data
|
||||||
|
|
||||||
def version_installed(self, addon):
|
def version_installed(self, addon):
|
||||||
"""Return installed version."""
|
"""Return installed version."""
|
||||||
return self._data[addon][ATTR_VERSION]
|
return self._user_data[addon][ATTR_VERSION]
|
||||||
|
|
||||||
def set_install_addon(self, addon, version):
|
def set_addon_install(self, addon, version):
|
||||||
"""Set addon as installed."""
|
"""Set addon as installed."""
|
||||||
self._data[addon] = {
|
self._system_data[addon] = self._current_data[addon]
|
||||||
|
self._user_data[addon] = {
|
||||||
|
ATTR_OPTIONS: {},
|
||||||
ATTR_VERSION: version,
|
ATTR_VERSION: version,
|
||||||
ATTR_OPTIONS: {}
|
|
||||||
}
|
}
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def set_uninstall_addon(self, addon):
|
def set_addon_uninstall(self, addon):
|
||||||
"""Set addon as uninstalled."""
|
"""Set addon as uninstalled."""
|
||||||
self._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._system_data[addon] = self._current_data[addon]
|
||||||
|
self._user_data[addon][ATTR_VERSION] = version
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def set_options(self, addon, options):
|
def set_options(self, addon, options):
|
||||||
"""Store user addon options."""
|
"""Store user addon options."""
|
||||||
self._data[addon][ATTR_OPTIONS] = options
|
self._user_data[addon][ATTR_OPTIONS] = options
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def set_version(self, addon, version):
|
def set_boot(self, addon, boot):
|
||||||
"""Update version of addon."""
|
"""Store user boot options."""
|
||||||
self._data[addon][ATTR_VERSION] = version
|
self._user_data[addon][ATTR_BOOT] = boot
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def get_options(self, addon):
|
def get_options(self, addon):
|
||||||
"""Return options with local changes."""
|
"""Return options with local changes."""
|
||||||
opt = self._addons_data[addon][ATTR_OPTIONS]
|
return {
|
||||||
if addon in self._data:
|
**self._system_data[addon][ATTR_OPTIONS],
|
||||||
opt.update(self._data[addon][ATTR_OPTIONS])
|
**self._user_data[addon][ATTR_OPTIONS],
|
||||||
return opt
|
}
|
||||||
|
|
||||||
def get_boot(self, addon):
|
def get_boot(self, addon):
|
||||||
"""Return boot config with prio local settings."""
|
"""Return boot config with prio local settings."""
|
||||||
if ATTR_BOOT in self._data[addon]:
|
if ATTR_BOOT in self._user_data[addon]:
|
||||||
return self._data[addon][ATTR_BOOT]
|
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):
|
def get_name(self, addon):
|
||||||
"""Return name of addon."""
|
"""Return name of addon."""
|
||||||
return self._addons_data[addon][ATTR_NAME]
|
return self._system_data[addon][ATTR_NAME]
|
||||||
|
|
||||||
def get_description(self, addon):
|
def get_description(self, addon):
|
||||||
"""Return description of 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."""
|
"""Return version of addon."""
|
||||||
return self._addons_data[addon][ATTR_VERSION]
|
if addon not in self._current_data:
|
||||||
|
return self.version_installed(addon)
|
||||||
def get_slug(self, addon):
|
return self._current_data[addon][ATTR_VERSION]
|
||||||
"""Return slug of addon."""
|
|
||||||
return self._addons_data[addon][ATTR_SLUG]
|
|
||||||
|
|
||||||
def get_ports(self, addon):
|
def get_ports(self, addon):
|
||||||
"""Return ports of 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):
|
def get_image(self, addon):
|
||||||
"""Return image name of addon."""
|
"""Return image name of addon."""
|
||||||
if ATTR_IMAGE not in self._addons_data[addon]:
|
addon_data = self._system_data.get(addon, self._current_data[addon])
|
||||||
return "{}/{}-addon-{}".format(
|
|
||||||
DOCKER_REPO, self.arch, self.get_slug(addon))
|
|
||||||
|
|
||||||
return self._addons_data[addon][ATTR_IMAGE]
|
if ATTR_IMAGE not in addon_data:
|
||||||
|
return "{}/{}-addon-{}".format(DOCKER_REPO, self.arch, addon)
|
||||||
|
|
||||||
|
return addon_data[ATTR_IMAGE]
|
||||||
|
|
||||||
def need_config(self, addon):
|
def need_config(self, addon):
|
||||||
"""Return True if config map is needed."""
|
"""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):
|
def need_ssl(self, addon):
|
||||||
"""Return True if ssl map is needed."""
|
"""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):
|
def path_data(self, addon):
|
||||||
"""Return addon data path inside supervisor."""
|
"""Return addon data path inside supervisor."""
|
||||||
return "{}/{}".format(
|
return "{}/{}".format(self.config.path_addons_data, addon)
|
||||||
self.config.path_addons_data, self._addons_data[addon][ATTR_SLUG])
|
|
||||||
|
|
||||||
def path_data_docker(self, addon):
|
def path_data_docker(self, addon):
|
||||||
"""Return addon data path external for docker."""
|
"""Return addon data path external for docker."""
|
||||||
return "{}/{}".format(self.config.path_addons_data_docker,
|
return "{}/{}".format(self.config.path_addons_data_docker, addon)
|
||||||
self._addons_data[addon][ATTR_SLUG])
|
|
||||||
|
|
||||||
def path_addon_options(self, addon):
|
def path_addon_options(self, addon):
|
||||||
"""Return path to addons options."""
|
"""Return path to addons options."""
|
||||||
@@ -220,7 +238,7 @@ class AddonsData(Config):
|
|||||||
|
|
||||||
def get_schema(self, addon):
|
def get_schema(self, addon):
|
||||||
"""Create a schema for addon options."""
|
"""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)))
|
schema = vol.Schema(vol.All(dict, validate_options(raw_schema)))
|
||||||
return schema
|
return schema
|
||||||
|
@@ -25,26 +25,26 @@ class RestAPI(object):
|
|||||||
self._handler = None
|
self._handler = None
|
||||||
self.server = None
|
self.server = None
|
||||||
|
|
||||||
def register_host(self, host_controll):
|
def register_host(self, host_control):
|
||||||
"""Register hostcontroll function."""
|
"""Register hostcontrol function."""
|
||||||
api_host = APIHost(self.config, self.loop, host_controll)
|
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/info', api_host.info)
|
||||||
self.webapp.router.add_get('/host/reboot', api_host.reboot)
|
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/shutdown', api_host.shutdown)
|
||||||
self.webapp.router.add_get('/host/update', api_host.update)
|
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."""
|
"""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/info', api_net.info)
|
||||||
self.webapp.router.add_get('/network/options', api_net.options)
|
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."""
|
"""Register supervisor function."""
|
||||||
api_supervisor = APISupervisor(
|
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/ping', api_supervisor.ping)
|
||||||
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)
|
self.webapp.router.add_get('/supervisor/info', api_supervisor.info)
|
||||||
@@ -52,6 +52,7 @@ class RestAPI(object):
|
|||||||
self.webapp.router.add_get('/supervisor/reload', api_supervisor.reload)
|
self.webapp.router.add_get('/supervisor/reload', api_supervisor.reload)
|
||||||
self.webapp.router.add_get(
|
self.webapp.router.add_get(
|
||||||
'/supervisor/options', api_supervisor.options)
|
'/supervisor/options', api_supervisor.options)
|
||||||
|
self.webapp.router.add_get('/supervisor/logs', api_supervisor.logs)
|
||||||
|
|
||||||
def register_homeassistant(self, dock_homeassistant):
|
def register_homeassistant(self, dock_homeassistant):
|
||||||
"""Register homeassistant function."""
|
"""Register homeassistant function."""
|
||||||
@@ -59,6 +60,7 @@ class RestAPI(object):
|
|||||||
|
|
||||||
self.webapp.router.add_get('/homeassistant/info', api_hass.info)
|
self.webapp.router.add_get('/homeassistant/info', api_hass.info)
|
||||||
self.webapp.router.add_get('/homeassistant/update', api_hass.update)
|
self.webapp.router.add_get('/homeassistant/update', api_hass.update)
|
||||||
|
self.webapp.router.add_get('/homeassistant/logs', api_hass.logs)
|
||||||
|
|
||||||
def register_addons(self, addons):
|
def register_addons(self, addons):
|
||||||
"""Register homeassistant function."""
|
"""Register homeassistant function."""
|
||||||
@@ -74,6 +76,7 @@ class RestAPI(object):
|
|||||||
self.webapp.router.add_get('/addons/{addon}/update', api_addons.update)
|
self.webapp.router.add_get('/addons/{addon}/update', api_addons.update)
|
||||||
self.webapp.router.add_get(
|
self.webapp.router.add_get(
|
||||||
'/addons/{addon}/options', api_addons.options)
|
'/addons/{addon}/options', api_addons.options)
|
||||||
|
self.webapp.router.add_get('/addons/{addon}/logs', api_addons.logs)
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""Run rest api webserver."""
|
"""Run rest api webserver."""
|
||||||
|
@@ -5,10 +5,10 @@ import logging
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from voluptuous.humanize import humanize_error
|
from voluptuous.humanize import humanize_error
|
||||||
|
|
||||||
from .util import api_process, api_validate
|
from .util import api_process, api_process_raw, api_validate
|
||||||
from ..const import (
|
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)
|
STATE_STOPPED, STATE_STARTED, BOOT_AUTO, BOOT_MANUAL)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -16,6 +16,10 @@ SCHEMA_VERSION = vol.Schema({
|
|||||||
vol.Optional(ATTR_VERSION): vol.Coerce(str),
|
vol.Optional(ATTR_VERSION): vol.Coerce(str),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
SCHEMA_OPTIONS = vol.Schema({
|
||||||
|
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL])
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class APIAddons(object):
|
class APIAddons(object):
|
||||||
"""Handle rest api for addons functions."""
|
"""Handle rest api for addons functions."""
|
||||||
@@ -43,23 +47,31 @@ class APIAddons(object):
|
|||||||
"""Return addon information."""
|
"""Return addon information."""
|
||||||
addon = self._extract_addon(request)
|
addon = self._extract_addon(request)
|
||||||
|
|
||||||
info = {
|
return {
|
||||||
ATTR_VERSION: self.addons.version_installed(addon),
|
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_STATE: await self.addons.state(addon),
|
||||||
ATTR_BOOT: self.addons.get_boot(addon),
|
ATTR_BOOT: self.addons.get_boot(addon),
|
||||||
ATTR_OPTIONS: self.addons.get_options(addon),
|
ATTR_OPTIONS: self.addons.get_options(addon),
|
||||||
}
|
}
|
||||||
return info
|
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
async def options(self, request):
|
async def options(self, request):
|
||||||
"""Store user options for addon."""
|
"""Store user options for addon."""
|
||||||
addon = self._extract_addon(request)
|
addon = self._extract_addon(request)
|
||||||
schema = self.addons.get_schema(addon)
|
options_schema = self.addons.get_schema(addon)
|
||||||
|
|
||||||
|
addon_schema = SCHEMA_OPTIONS.extend({
|
||||||
|
vol.Optional(ATTR_OPTIONS): options_schema,
|
||||||
|
})
|
||||||
|
|
||||||
|
body = await api_validate(addon_schema, request)
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
options = await api_validate(schema, request)
|
|
||||||
self.addons.set_options(addon, options)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
@@ -68,7 +80,7 @@ class APIAddons(object):
|
|||||||
body = await api_validate(SCHEMA_VERSION, request)
|
body = await api_validate(SCHEMA_VERSION, request)
|
||||||
addon = self._extract_addon(request, check_installed=False)
|
addon = self._extract_addon(request, check_installed=False)
|
||||||
version = body.get(
|
version = body.get(
|
||||||
ATTR_VERSION, self.addons.get_version(addon))
|
ATTR_VERSION, self.addons.get_last_version(addon))
|
||||||
|
|
||||||
return await asyncio.shield(
|
return await asyncio.shield(
|
||||||
self.addons.install(addon, version), loop=self.loop)
|
self.addons.install(addon, version), loop=self.loop)
|
||||||
@@ -117,10 +129,16 @@ class APIAddons(object):
|
|||||||
body = await api_validate(SCHEMA_VERSION, request)
|
body = await api_validate(SCHEMA_VERSION, request)
|
||||||
addon = self._extract_addon(request)
|
addon = self._extract_addon(request)
|
||||||
version = body.get(
|
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):
|
if version == self.addons.version_installed(addon):
|
||||||
raise RuntimeError("Version is already in use")
|
raise RuntimeError("Version is already in use")
|
||||||
|
|
||||||
return await asyncio.shield(
|
return await asyncio.shield(
|
||||||
self.addons.update(addon, version), loop=self.loop)
|
self.addons.update(addon, version), loop=self.loop)
|
||||||
|
|
||||||
|
@api_process_raw
|
||||||
|
def logs(self, request):
|
||||||
|
"""Return logs from addon."""
|
||||||
|
addon = self._extract_addon(request)
|
||||||
|
return self.addons.logs(addon)
|
||||||
|
@@ -4,8 +4,8 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from .util import api_process, api_validate
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -17,18 +17,18 @@ SCHEMA_VERSION = vol.Schema({
|
|||||||
class APIHomeAssistant(object):
|
class APIHomeAssistant(object):
|
||||||
"""Handle rest api for homeassistant functions."""
|
"""Handle rest api for homeassistant functions."""
|
||||||
|
|
||||||
def __init__(self, config, loop, dock_hass):
|
def __init__(self, config, loop, homeassistant):
|
||||||
"""Initialize homeassistant rest api part."""
|
"""Initialize homeassistant rest api part."""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.dock_hass = dock_hass
|
self.homeassistant = homeassistant
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
async def info(self, request):
|
async def info(self, request):
|
||||||
"""Return host information."""
|
"""Return host information."""
|
||||||
info = {
|
info = {
|
||||||
ATTR_VERSION: self.dock_hass.version,
|
ATTR_VERSION: self.homeassistant.version,
|
||||||
ATTR_CURRENT: self.config.current_homeassistant,
|
ATTR_LAST_VERSION: self.config.last_homeassistant,
|
||||||
}
|
}
|
||||||
|
|
||||||
return info
|
return info
|
||||||
@@ -37,13 +37,21 @@ class APIHomeAssistant(object):
|
|||||||
async def update(self, request):
|
async def update(self, request):
|
||||||
"""Update host OS."""
|
"""Update host OS."""
|
||||||
body = await api_validate(SCHEMA_VERSION, request)
|
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.dock_hass.in_progress:
|
if self.homeassistant.in_progress:
|
||||||
raise RuntimeError("Other task is in progress")
|
raise RuntimeError("Other task is in progress")
|
||||||
|
|
||||||
if version == self.dock_hass.version:
|
if version == self.homeassistant.version:
|
||||||
raise RuntimeError("Version is already in use")
|
raise RuntimeError("Version is already in use")
|
||||||
|
|
||||||
return await asyncio.shield(
|
return await asyncio.shield(
|
||||||
self.dock_hass.update(version), loop=self.loop)
|
self.homeassistant.update(version), loop=self.loop)
|
||||||
|
|
||||||
|
@api_process_raw
|
||||||
|
def logs(self, request):
|
||||||
|
"""Return homeassistant docker logs.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
return self.homeassistant.logs()
|
||||||
|
@@ -3,13 +3,13 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from .util import api_process_hostcontroll, api_process, api_validate
|
from .util import api_process_hostcontrol, api_process, api_validate
|
||||||
from ..const import ATTR_VERSION
|
from ..const import (
|
||||||
|
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_TYPE, ATTR_HOSTNAME, ATTR_FEATURES,
|
||||||
|
ATTR_OS)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
UNKNOWN = 'unknown'
|
|
||||||
|
|
||||||
SCHEMA_VERSION = vol.Schema({
|
SCHEMA_VERSION = vol.Schema({
|
||||||
vol.Optional(ATTR_VERSION): vol.Coerce(str),
|
vol.Optional(ATTR_VERSION): vol.Coerce(str),
|
||||||
})
|
})
|
||||||
@@ -18,44 +18,41 @@ SCHEMA_VERSION = vol.Schema({
|
|||||||
class APIHost(object):
|
class APIHost(object):
|
||||||
"""Handle rest api for host functions."""
|
"""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."""
|
"""Initialize host rest api part."""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.host_controll = host_controll
|
self.host_control = host_control
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
async def info(self, request):
|
async def info(self, request):
|
||||||
"""Return host information."""
|
"""Return host information."""
|
||||||
if not self.host_controll.active:
|
return {
|
||||||
info = {
|
ATTR_TYPE: self.host_control.type,
|
||||||
'os': UNKNOWN,
|
ATTR_VERSION: self.host_control.version,
|
||||||
'version': UNKNOWN,
|
ATTR_LAST_VERSION: self.host_control.last,
|
||||||
'current': UNKNOWN,
|
ATTR_FEATURES: self.host_control.features,
|
||||||
'level': 0,
|
ATTR_HOSTNAME: self.host_control.hostname,
|
||||||
'hostname': UNKNOWN,
|
ATTR_OS: self.host_control.os_info,
|
||||||
}
|
}
|
||||||
return info
|
|
||||||
|
|
||||||
return await self.host_controll.info()
|
@api_process_hostcontrol
|
||||||
|
|
||||||
@api_process_hostcontroll
|
|
||||||
def reboot(self, request):
|
def reboot(self, request):
|
||||||
"""Reboot host."""
|
"""Reboot host."""
|
||||||
return self.host_controll.reboot()
|
return self.host_control.reboot()
|
||||||
|
|
||||||
@api_process_hostcontroll
|
@api_process_hostcontrol
|
||||||
def shutdown(self, request):
|
def shutdown(self, request):
|
||||||
"""Poweroff host."""
|
"""Poweroff host."""
|
||||||
return self.host_controll.shutdown()
|
return self.host_control.shutdown()
|
||||||
|
|
||||||
@api_process_hostcontroll
|
@api_process_hostcontrol
|
||||||
async def update(self, request):
|
async def update(self, request):
|
||||||
"""Update host OS."""
|
"""Update host OS."""
|
||||||
body = await api_validate(SCHEMA_VERSION, request)
|
body = await api_validate(SCHEMA_VERSION, request)
|
||||||
version = body.get(ATTR_VERSION)
|
version = body.get(ATTR_VERSION)
|
||||||
|
|
||||||
if version == self.host_controll.version:
|
if version == self.host_control.version:
|
||||||
raise RuntimeError("Version is already in use")
|
raise RuntimeError("Version is already in use")
|
||||||
|
|
||||||
return await self.host_controll.host_update(version=version)
|
return await self.host_control.update(version=version)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
"""Init file for HassIO network rest api."""
|
"""Init file for HassIO network rest api."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .util import api_process_hostcontroll
|
from .util import api_process_hostcontrol
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -9,18 +9,18 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
class APINetwork(object):
|
class APINetwork(object):
|
||||||
"""Handle rest api for network functions."""
|
"""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."""
|
"""Initialize network rest api part."""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.host_controll = host_controll
|
self.host_control = host_control
|
||||||
|
|
||||||
@api_process_hostcontroll
|
@api_process_hostcontrol
|
||||||
def info(self, request):
|
def info(self, request):
|
||||||
"""Show network settings."""
|
"""Show network settings."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@api_process_hostcontroll
|
@api_process_hostcontrol
|
||||||
def options(self, request):
|
def options(self, request):
|
||||||
"""Edit network settings."""
|
"""Edit network settings."""
|
||||||
pass
|
pass
|
||||||
|
@@ -4,15 +4,16 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from .util import api_process, api_validate
|
from .util import api_process, api_process_raw, api_validate
|
||||||
from ..const import (
|
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__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
SCHEMA_OPTIONS = vol.Schema({
|
SCHEMA_OPTIONS = vol.Schema({
|
||||||
# pylint: disable=no-value-for-parameter
|
# pylint: disable=no-value-for-parameter
|
||||||
vol.Optional(ATTR_BETA): vol.Boolean(),
|
vol.Optional(ATTR_BETA_CHANNEL): vol.Boolean(),
|
||||||
})
|
})
|
||||||
|
|
||||||
SCHEMA_VERSION = vol.Schema({
|
SCHEMA_VERSION = vol.Schema({
|
||||||
@@ -23,12 +24,13 @@ SCHEMA_VERSION = vol.Schema({
|
|||||||
class APISupervisor(object):
|
class APISupervisor(object):
|
||||||
"""Handle rest api for supervisor functions."""
|
"""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."""
|
"""Initialize supervisor rest api part."""
|
||||||
self.config = config
|
self.config = config
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.supervisor = supervisor
|
self.supervisor = supervisor
|
||||||
self.addons = addons
|
self.addons = addons
|
||||||
|
self.host_control = host_control
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
async def ping(self, request):
|
async def ping(self, request):
|
||||||
@@ -38,21 +40,20 @@ class APISupervisor(object):
|
|||||||
@api_process
|
@api_process
|
||||||
async def info(self, request):
|
async def info(self, request):
|
||||||
"""Return host information."""
|
"""Return host information."""
|
||||||
info = {
|
return {
|
||||||
ATTR_VERSION: HASSIO_VERSION,
|
ATTR_VERSION: HASSIO_VERSION,
|
||||||
ATTR_CURRENT: self.config.current_hassio,
|
ATTR_LAST_VERSION: self.config.last_hassio,
|
||||||
ATTR_BETA: self.config.upstream_beta,
|
ATTR_BETA_CHANNEL: self.config.upstream_beta,
|
||||||
ATTR_ADDONS: self.addons.list,
|
ATTR_ADDONS: self.addons.list_api,
|
||||||
}
|
}
|
||||||
return info
|
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
async def options(self, request):
|
async def options(self, request):
|
||||||
"""Set supervisor options."""
|
"""Set supervisor options."""
|
||||||
body = await api_validate(SCHEMA_OPTIONS, request)
|
body = await api_validate(SCHEMA_OPTIONS, request)
|
||||||
|
|
||||||
if ATTR_BETA in body:
|
if ATTR_BETA_CHANNEL in body:
|
||||||
self.config.upstream_beta = body[ATTR_BETA]
|
self.config.upstream_beta = body[ATTR_BETA_CHANNEL]
|
||||||
|
|
||||||
return self.config.save()
|
return self.config.save()
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ class APISupervisor(object):
|
|||||||
async def update(self, request):
|
async def update(self, request):
|
||||||
"""Update supervisor OS."""
|
"""Update supervisor OS."""
|
||||||
body = await api_validate(SCHEMA_VERSION, request)
|
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:
|
if version == self.supervisor.version:
|
||||||
raise RuntimeError("Version is already in use")
|
raise RuntimeError("Version is already in use")
|
||||||
@@ -71,7 +72,10 @@ class APISupervisor(object):
|
|||||||
@api_process
|
@api_process
|
||||||
async def reload(self, request):
|
async def reload(self, request):
|
||||||
"""Reload addons, config ect."""
|
"""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(
|
results, _ = await asyncio.shield(
|
||||||
asyncio.wait(tasks, loop=self.loop), loop=self.loop)
|
asyncio.wait(tasks, loop=self.loop), loop=self.loop)
|
||||||
|
|
||||||
@@ -80,3 +84,11 @@ class APISupervisor(object):
|
|||||||
raise RuntimeError("Some reload task fails!")
|
raise RuntimeError("Some reload task fails!")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@api_process_raw
|
||||||
|
def logs(self, request):
|
||||||
|
"""Return supervisor docker logs.
|
||||||
|
|
||||||
|
Return a coroutine.
|
||||||
|
"""
|
||||||
|
return self.supervisor.logs()
|
||||||
|
@@ -39,11 +39,11 @@ def api_process(method):
|
|||||||
return wrap_api
|
return wrap_api
|
||||||
|
|
||||||
|
|
||||||
def api_process_hostcontroll(method):
|
def api_process_hostcontrol(method):
|
||||||
"""Wrap HostControll calls to rest api."""
|
"""Wrap HostControl calls to rest api."""
|
||||||
async def wrap_hostcontroll(api, *args, **kwargs):
|
async def wrap_hostcontrol(api, *args, **kwargs):
|
||||||
"""Return host information."""
|
"""Return host information."""
|
||||||
if not api.host_controll.active:
|
if not api.host_control.active:
|
||||||
raise HTTPServiceUnavailable()
|
raise HTTPServiceUnavailable()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -59,7 +59,21 @@ def api_process_hostcontroll(method):
|
|||||||
return api_return_ok()
|
return api_return_ok()
|
||||||
return api_return_error()
|
return api_return_error()
|
||||||
|
|
||||||
return wrap_hostcontroll
|
return wrap_hostcontrol
|
||||||
|
|
||||||
|
|
||||||
|
def api_process_raw(method):
|
||||||
|
"""Wrap function with raw output to rest api."""
|
||||||
|
async def wrap_api(api, *args, **kwargs):
|
||||||
|
"""Return api information."""
|
||||||
|
try:
|
||||||
|
message = await method(api, *args, **kwargs)
|
||||||
|
except RuntimeError as err:
|
||||||
|
message = str(err).encode()
|
||||||
|
|
||||||
|
return web.Response(body=message)
|
||||||
|
|
||||||
|
return wrap_api
|
||||||
|
|
||||||
|
|
||||||
def api_return_error(message=None):
|
def api_return_error(message=None):
|
||||||
|
@@ -10,10 +10,10 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
HOMEASSISTANT_CONFIG = "{}/homeassistant"
|
HOMEASSISTANT_CONFIG = "{}/homeassistant"
|
||||||
HOMEASSISTANT_IMAGE = 'homeassistant_image'
|
HOMEASSISTANT_IMAGE = 'homeassistant_image'
|
||||||
HOMEASSISTANT_CURRENT = 'homeassistant_current'
|
HOMEASSISTANT_LAST = 'homeassistant_last'
|
||||||
|
|
||||||
HASSIO_SSL = "{}/ssl"
|
HASSIO_SSL = "{}/ssl"
|
||||||
HASSIO_CURRENT = 'hassio_current'
|
HASSIO_LAST = 'hassio_last'
|
||||||
HASSIO_CLEANUP = 'hassio_cleanup'
|
HASSIO_CLEANUP = 'hassio_cleanup'
|
||||||
|
|
||||||
ADDONS_REPO = "{}/addons"
|
ADDONS_REPO = "{}/addons"
|
||||||
@@ -67,13 +67,13 @@ class CoreConfig(Config):
|
|||||||
|
|
||||||
async def fetch_update_infos(self):
|
async def fetch_update_infos(self):
|
||||||
"""Read current versions from web."""
|
"""Read current versions from web."""
|
||||||
current = await fetch_current_versions(
|
last = await fetch_current_versions(
|
||||||
self.websession, beta=self.upstream_beta)
|
self.websession, beta=self.upstream_beta)
|
||||||
|
|
||||||
if current:
|
if last:
|
||||||
self._data.update({
|
self._data.update({
|
||||||
HOMEASSISTANT_CURRENT: current.get('homeassistant_tag'),
|
HOMEASSISTANT_LAST: last.get('homeassistant'),
|
||||||
HASSIO_CURRENT: current.get('hassio_tag'),
|
HASSIO_LAST: last.get('hassio'),
|
||||||
})
|
})
|
||||||
self.save()
|
self.save()
|
||||||
return True
|
return True
|
||||||
@@ -120,14 +120,14 @@ class CoreConfig(Config):
|
|||||||
return self._data.get(HOMEASSISTANT_IMAGE)
|
return self._data.get(HOMEASSISTANT_IMAGE)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_homeassistant(self):
|
def last_homeassistant(self):
|
||||||
"""Actual version of homeassistant."""
|
"""Actual version of homeassistant."""
|
||||||
return self._data.get(HOMEASSISTANT_CURRENT)
|
return self._data.get(HOMEASSISTANT_LAST)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_hassio(self):
|
def last_hassio(self):
|
||||||
"""Actual version of hassio."""
|
"""Actual version of hassio."""
|
||||||
return self._data.get(HASSIO_CURRENT)
|
return self._data.get(HASSIO_LAST)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_hassio_docker(self):
|
def path_hassio_docker(self):
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
"""Const file for HassIO."""
|
"""Const file for HassIO."""
|
||||||
HASSIO_VERSION = '0.11'
|
HASSIO_VERSION = '0.16'
|
||||||
|
|
||||||
URL_HASSIO_VERSION = \
|
URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/'
|
||||||
'https://raw.githubusercontent.com/pvizeli/hassio/master/version.json'
|
'hassio/master/version.json')
|
||||||
URL_HASSIO_VERSION_BETA = \
|
URL_HASSIO_VERSION_BETA = ('https://raw.githubusercontent.com/home-assistant/'
|
||||||
'https://raw.githubusercontent.com/pvizeli/hassio/master/version_beta.json'
|
'hassio/master/version_beta.json')
|
||||||
|
|
||||||
URL_HASSIO_ADDONS = 'https://github.com/pvizeli/hassio-addons'
|
URL_HASSIO_ADDONS = 'https://github.com/home-assistant/hassio-addons'
|
||||||
|
|
||||||
DOCKER_REPO = "pvizeli"
|
DOCKER_REPO = "pvizeli"
|
||||||
|
|
||||||
@@ -31,10 +31,14 @@ JSON_MESSAGE = 'message'
|
|||||||
RESULT_ERROR = 'error'
|
RESULT_ERROR = 'error'
|
||||||
RESULT_OK = 'ok'
|
RESULT_OK = 'ok'
|
||||||
|
|
||||||
|
ATTR_HOSTNAME = 'hostname'
|
||||||
|
ATTR_OS = 'os'
|
||||||
|
ATTR_TYPE = 'type'
|
||||||
|
ATTR_FEATURES = 'features'
|
||||||
ATTR_ADDONS = 'addons'
|
ATTR_ADDONS = 'addons'
|
||||||
ATTR_VERSION = 'version'
|
ATTR_VERSION = 'version'
|
||||||
ATTR_CURRENT = 'current'
|
ATTR_LAST_VERSION = 'last_version'
|
||||||
ATTR_BETA = 'beta'
|
ATTR_BETA_CHANNEL = 'beta_channel'
|
||||||
ATTR_NAME = 'name'
|
ATTR_NAME = 'name'
|
||||||
ATTR_SLUG = 'slug'
|
ATTR_SLUG = 'slug'
|
||||||
ATTR_DESCRIPTON = 'description'
|
ATTR_DESCRIPTON = 'description'
|
||||||
@@ -45,6 +49,7 @@ ATTR_MAP_CONFIG = 'map_config'
|
|||||||
ATTR_MAP_SSL = 'map_ssl'
|
ATTR_MAP_SSL = 'map_ssl'
|
||||||
ATTR_OPTIONS = 'options'
|
ATTR_OPTIONS = 'options'
|
||||||
ATTR_INSTALLED = 'installed'
|
ATTR_INSTALLED = 'installed'
|
||||||
|
ATTR_DEDICATED = 'dedicated'
|
||||||
ATTR_STATE = 'state'
|
ATTR_STATE = 'state'
|
||||||
ATTR_SCHEMA = 'schema'
|
ATTR_SCHEMA = 'schema'
|
||||||
ATTR_IMAGE = 'image'
|
ATTR_IMAGE = 'image'
|
||||||
|
@@ -8,7 +8,7 @@ import docker
|
|||||||
from . import bootstrap
|
from . import bootstrap
|
||||||
from .addons import AddonManager
|
from .addons import AddonManager
|
||||||
from .api import RestAPI
|
from .api import RestAPI
|
||||||
from .host_controll import HostControll
|
from .host_control import HostControl
|
||||||
from .const import (
|
from .const import (
|
||||||
SOCKET_DOCKER, RUN_UPDATE_INFO_TASKS, RUN_RELOAD_ADDONS_TASKS,
|
SOCKET_DOCKER, RUN_UPDATE_INFO_TASKS, RUN_RELOAD_ADDONS_TASKS,
|
||||||
RUN_UPDATE_SUPERVISOR_TASKS, STARTUP_AFTER, STARTUP_BEFORE)
|
RUN_UPDATE_SUPERVISOR_TASKS, STARTUP_AFTER, STARTUP_BEFORE)
|
||||||
@@ -40,8 +40,8 @@ class HassIO(object):
|
|||||||
self.homeassistant = DockerHomeAssistant(
|
self.homeassistant = DockerHomeAssistant(
|
||||||
self.config, self.loop, self.dock)
|
self.config, self.loop, self.dock)
|
||||||
|
|
||||||
# init HostControll
|
# init HostControl
|
||||||
self.host_controll = HostControll(self.loop)
|
self.host_control = HostControl(self.loop)
|
||||||
|
|
||||||
# init addon system
|
# init addon system
|
||||||
self.addons = AddonManager(self.config, self.loop, self.dock)
|
self.addons = AddonManager(self.config, self.loop, self.dock)
|
||||||
@@ -55,20 +55,19 @@ class HassIO(object):
|
|||||||
# set api endpoint
|
# set api endpoint
|
||||||
self.config.api_endpoint = await get_local_ip(self.loop)
|
self.config.api_endpoint = await get_local_ip(self.loop)
|
||||||
|
|
||||||
# hostcontroll
|
# hostcontrol
|
||||||
host_info = await self.host_controll.info()
|
await self.host_control.load()
|
||||||
if host_info:
|
_LOGGER.info(
|
||||||
self.host_controll.version = host_info.get('version')
|
"Connected to HostControl. Type: %s Version: %s Hostname: %s "
|
||||||
_LOGGER.info(
|
"Features: %s", self.host_control.type,
|
||||||
"Connected to HostControll. OS: %s Version: %s Hostname: %s "
|
self.host_control.version, self.host_control.hostname,
|
||||||
"Feature-lvl: %d", host_info.get('os'),
|
self.host_control.features)
|
||||||
host_info.get('version'), host_info.get('hostname'),
|
|
||||||
host_info.get('level', 0))
|
|
||||||
|
|
||||||
# rest api views
|
# rest api views
|
||||||
self.api.register_host(self.host_controll)
|
self.api.register_host(self.host_control)
|
||||||
self.api.register_network(self.host_controll)
|
self.api.register_network(self.host_control)
|
||||||
self.api.register_supervisor(self.supervisor, self.addons)
|
self.api.register_supervisor(
|
||||||
|
self.supervisor, self.addons, self.host_control)
|
||||||
self.api.register_homeassistant(self.homeassistant)
|
self.api.register_homeassistant(self.homeassistant)
|
||||||
self.api.register_addons(self.addons)
|
self.api.register_addons(self.addons)
|
||||||
|
|
||||||
@@ -130,10 +129,10 @@ class HassIO(object):
|
|||||||
"""Install a homeassistant docker container."""
|
"""Install a homeassistant docker container."""
|
||||||
while True:
|
while True:
|
||||||
# read homeassistant tag and install it
|
# read homeassistant tag and install it
|
||||||
if not self.config.current_homeassistant:
|
if not self.config.last_homeassistant:
|
||||||
await self.config.fetch_update_infos()
|
await self.config.fetch_update_infos()
|
||||||
|
|
||||||
tag = self.config.current_homeassistant
|
tag = self.config.last_homeassistant
|
||||||
if tag and await self.homeassistant.install(tag):
|
if tag and await self.homeassistant.install(tag):
|
||||||
break
|
break
|
||||||
_LOGGER.warning("Error on setup HomeAssistant. Retry in 60.")
|
_LOGGER.warning("Error on setup HomeAssistant. Retry in 60.")
|
||||||
@@ -144,9 +143,9 @@ class HassIO(object):
|
|||||||
|
|
||||||
async def _hassio_update(self):
|
async def _hassio_update(self):
|
||||||
"""Check and run update of supervisor hassio."""
|
"""Check and run update of supervisor hassio."""
|
||||||
if self.config.current_hassio == self.supervisor.version:
|
if self.config.last_hassio == self.supervisor.version:
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Found new HassIO version %s.", self.config.current_hassio)
|
"Found new HassIO version %s.", self.config.last_hassio)
|
||||||
await self.supervisor.update(self.config.current_hassio)
|
await self.supervisor.update(self.config.last_hassio)
|
||||||
|
@@ -99,8 +99,9 @@ class DockerBase(object):
|
|||||||
self.container.attrs['Config']['Env'])
|
self.container.attrs['Config']['Env'])
|
||||||
except docker.errors.DockerException:
|
except docker.errors.DockerException:
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
self.container.reload()
|
||||||
|
|
||||||
self.container.reload()
|
|
||||||
return self.container.status == 'running'
|
return self.container.status == 'running'
|
||||||
|
|
||||||
async def attach(self):
|
async def attach(self):
|
||||||
@@ -222,7 +223,6 @@ class DockerBase(object):
|
|||||||
|
|
||||||
Need run inside executor.
|
Need run inside executor.
|
||||||
"""
|
"""
|
||||||
old_run = self._is_running()
|
|
||||||
old_image = "{}:{}".format(self.image, self.version)
|
old_image = "{}:{}".format(self.image, self.version)
|
||||||
|
|
||||||
_LOGGER.info("Update docker %s with %s:%s",
|
_LOGGER.info("Update docker %s with %s:%s",
|
||||||
@@ -237,9 +237,28 @@ class DockerBase(object):
|
|||||||
except docker.errors.DockerException as err:
|
except docker.errors.DockerException as err:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Can't remove old image %s -> %s", old_image, err)
|
"Can't remove old image %s -> %s", old_image, err)
|
||||||
# restore
|
|
||||||
if old_run:
|
|
||||||
self._run()
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def logs(self):
|
||||||
|
"""Return docker logs of container."""
|
||||||
|
if self._lock.locked():
|
||||||
|
_LOGGER.error("Can't excute logs while a task is in progress")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async with self._lock:
|
||||||
|
return await self.loop.run_in_executor(None, self._logs)
|
||||||
|
|
||||||
|
def _logs(self):
|
||||||
|
"""Return docker logs of container.
|
||||||
|
|
||||||
|
Need run inside executor.
|
||||||
|
"""
|
||||||
|
if not self.container:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.container.logs(tail=100, stdout=True, stderr=True)
|
||||||
|
except docker.errors.DockerException as err:
|
||||||
|
_LOGGER.warning("Can't grap logs from %s -> %s", self.image, err)
|
||||||
|
@@ -24,7 +24,7 @@ class DockerAddon(DockerBase):
|
|||||||
@property
|
@property
|
||||||
def docker_name(self):
|
def docker_name(self):
|
||||||
"""Return name of docker container."""
|
"""Return name of docker container."""
|
||||||
return "addon_{}".format(self.addons_data.get_slug(self.addon))
|
return "addon_{}".format(self.addon)
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
"""Run docker image.
|
"""Run docker image.
|
||||||
@@ -74,3 +74,17 @@ class DockerAddon(DockerBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _attach(self):
|
||||||
|
"""Attach to running docker container.
|
||||||
|
|
||||||
|
Need run inside executor.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.container = self.dock.containers.get(self.docker_name)
|
||||||
|
self.version = get_version_from_env(
|
||||||
|
self.container.attrs['Config']['Env'])
|
||||||
|
_LOGGER.info("Attach to image %s with version %s",
|
||||||
|
self.image, self.version)
|
||||||
|
except (docker.errors.DockerException, KeyError):
|
||||||
|
pass
|
||||||
|
@@ -62,3 +62,16 @@ class DockerHomeAssistant(DockerBase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
async def update(self, tag):
|
||||||
|
"""Update homeassistant docker image."""
|
||||||
|
if self._lock.locked():
|
||||||
|
_LOGGER.error("Can't excute update while a task is in progress")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async with self._lock:
|
||||||
|
if await self.loop.run_in_executor(None, self._update, tag):
|
||||||
|
await self.loop.run_in_executor(None, self._run)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
"""Host controll for HassIO."""
|
"""Host control for HassIO."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@@ -7,25 +7,35 @@ import stat
|
|||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
from .const import SOCKET_HC
|
from .const import (
|
||||||
|
SOCKET_HC, ATTR_LAST_VERSION, ATTR_VERSION, ATTR_TYPE, ATTR_FEATURES,
|
||||||
|
ATTR_HOSTNAME, ATTR_OS)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
TIMEOUT = 15
|
TIMEOUT = 15
|
||||||
|
UNKNOWN = 'unknown'
|
||||||
|
|
||||||
LEVEL_POWER = 1
|
FEATURES_SHUTDOWN = 'shutdown'
|
||||||
LEVEL_UPDATE_HOST = 2
|
FEATURES_REBOOT = 'reboot'
|
||||||
LEVEL_NETWORK = 4
|
FEATURES_UPDATE = 'update'
|
||||||
|
FEATURES_NETWORK_INFO = 'network_info'
|
||||||
|
FEATURES_NETWORK_CONTROL = 'network_control'
|
||||||
|
|
||||||
|
|
||||||
class HostControll(object):
|
class HostControl(object):
|
||||||
"""Client for host controll."""
|
"""Client for host control."""
|
||||||
|
|
||||||
def __init__(self, loop):
|
def __init__(self, loop):
|
||||||
"""Initialize HostControll socket client."""
|
"""Initialize HostControl socket client."""
|
||||||
self.loop = loop
|
self.loop = loop
|
||||||
self.active = False
|
self.active = False
|
||||||
self.version = None
|
self.version = UNKNOWN
|
||||||
|
self.last = UNKNOWN
|
||||||
|
self.type = UNKNOWN
|
||||||
|
self.features = []
|
||||||
|
self.hostname = UNKNOWN
|
||||||
|
self.os_info = UNKNOWN
|
||||||
|
|
||||||
mode = os.stat(SOCKET_HC)[stat.ST_MODE]
|
mode = os.stat(SOCKET_HC)[stat.ST_MODE]
|
||||||
if stat.S_ISSOCK(mode):
|
if stat.S_ISSOCK(mode):
|
||||||
@@ -44,14 +54,14 @@ class HostControll(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# send
|
# send
|
||||||
_LOGGER.info("Send '%s' to HostControll.", command)
|
_LOGGER.info("Send '%s' to HostControl.", command)
|
||||||
|
|
||||||
with async_timeout.timeout(TIMEOUT, loop=self.loop):
|
with async_timeout.timeout(TIMEOUT, loop=self.loop):
|
||||||
writer.write("{}\n".format(command).encode())
|
writer.write("{}\n".format(command).encode())
|
||||||
data = await reader.readline()
|
data = await reader.readline()
|
||||||
|
|
||||||
response = data.decode()
|
response = data.decode()
|
||||||
_LOGGER.debug("Receive from HostControll: %s.", response)
|
_LOGGER.debug("Receive from HostControl: %s.", response)
|
||||||
|
|
||||||
if response == "OK":
|
if response == "OK":
|
||||||
return True
|
return True
|
||||||
@@ -63,20 +73,29 @@ class HostControll(object):
|
|||||||
try:
|
try:
|
||||||
return json.loads(response)
|
return json.loads(response)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
_LOGGER.warning("Json parse error from HostControll.")
|
_LOGGER.warning("Json parse error from HostControl.")
|
||||||
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
_LOGGER.error("Timeout from HostControll!")
|
_LOGGER.error("Timeout from HostControl!")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
writer.close()
|
writer.close()
|
||||||
|
|
||||||
def info(self):
|
async def load(self):
|
||||||
"""Return Info from host.
|
"""Load Info from host.
|
||||||
|
|
||||||
Return a coroutine.
|
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)
|
||||||
|
self.os_info = info.get(ATTR_OS, UNKNOWN)
|
||||||
|
|
||||||
def reboot(self):
|
def reboot(self):
|
||||||
"""Reboot the host system.
|
"""Reboot the host system.
|
||||||
@@ -92,11 +111,11 @@ class HostControll(object):
|
|||||||
"""
|
"""
|
||||||
return self._send_command("shutdown")
|
return self._send_command("shutdown")
|
||||||
|
|
||||||
def host_update(self, version=None):
|
def update(self, version=None):
|
||||||
"""Update the host system.
|
"""Update the host system.
|
||||||
|
|
||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
if version:
|
if version:
|
||||||
return self._send_command("host-update {}".format(version))
|
return self._send_command("update {}".format(version))
|
||||||
return self._send_command("host-update")
|
return self._send_command("update")
|
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"hassio_tag": "0.11",
|
"hassio": "0.15",
|
||||||
"homeassistant_tag": "0.43",
|
"homeassistant": "0.43.1",
|
||||||
"resinos_version": "0.4",
|
"resinos": "0.4",
|
||||||
"resinhup_version": "0.1"
|
"resinhup": "0.1",
|
||||||
|
"generic": "0.2"
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"hassio_tag": "0.11",
|
"hassio": "0.15",
|
||||||
"homeassistant_tag": "0.43",
|
"homeassistant": "0.43.1",
|
||||||
"resinos_version": "0.4",
|
"resinos": "0.4",
|
||||||
"resinhup_version": "0.1"
|
"resinhup": "0.1",
|
||||||
|
"generic": "0.2"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user