Compare commits

...

73 Commits
103 ... 109

Author SHA1 Message Date
Pascal Vizeli
8517b43e85 Merge pull request #526 from home-assistant/dev
Release 109
2018-06-23 00:58:29 +02:00
Pascal Vizeli
033ea4e7dc Panel with HassOS support (#525) 2018-06-23 00:52:19 +02:00
Pascal Vizeli
a0c9e5ad26 HassOS support (#522)
* Add support for hassos

* Name command

* Update host.py

* Create hassos.py

* Update const.py

* Update host.py

* Update API.md

* Update const.py

* Update __init__.py

* Update hassos.py

* Update hassos.py

* Update hassos.py

* Update hassos.py

* Update const.py

* Update API.md

* Update hassos.py

* Update hassos.py

* Update API.md

* Update const.py

* Update hassos.py

* Update __init__.py

* fix lint

* Fix lint v2

* remove old function

* fix attribute error

* inittialize hassos

* Fix link

* fix error handling

* Fix handling
2018-06-22 22:54:03 +02:00
Pascal Vizeli
408d6eafcc Bump version to 109 2018-06-21 12:21:59 +02:00
Pascal Vizeli
054e357483 Add support to map devicetree into add-on (#519)
* Add support to map devicetree into add-on

* Update const.py

* Update validate.py

* Update addon.py

* Update addons.py

* Update API.md
2018-06-21 12:19:14 +02:00
Pascal Vizeli
cb520bff23 Merge pull request #518 from home-assistant/fix-device-configs
Bugfix home-assistant config with devices
2018-06-21 12:18:20 +02:00
Pascal Vizeli
024ebe0026 Update homeassistant.py 2018-06-21 12:00:13 +02:00
Pascal Vizeli
7b62e2f07b Bugfix home-assistant config with devices 2018-06-21 11:51:04 +02:00
Pascal Vizeli
7d52b3ba01 Merge pull request #517 from home-assistant/dev
Release 108
2018-06-21 11:33:09 +02:00
Pascal Vizeli
46caa23319 Update version.json 2018-06-21 11:27:19 +02:00
Pascal Vizeli
9aa5eda2c8 Some bugfix (#516)
* Some bugfix

* Update apparmor.py

* Update apparmor.py

* Update apparmor.py

* Update apparmor.py
2018-06-20 23:25:08 +02:00
Pascal Vizeli
f48182a69c Add lost panel files (#515)
* Update Panel for 0.72

* Add new panel files
2018-06-20 21:07:33 +02:00
Pascal Vizeli
788f883490 Update Panel for 0.72 (#514) 2018-06-20 20:37:26 +02:00
Pascal Vizeli
e84e82d018 Fix aiohttp handling (#512) 2018-06-20 15:32:55 +02:00
Pascal Vizeli
20e73796b8 Change aiohttp handling for server (#511) 2018-06-20 12:17:33 +02:00
Pascal Vizeli
7769d6fff1 Cleanup and fixup Apparmor implementation (#509)
* Cleanup and fixup Apparmor implementation

* Update addon.py

* Update validate.py

* Create apparmor.py

* Update exceptions.py

* Update apparmor.py

* Create apparmor.py

* Update const.py

* Update bootstrap.py

* Update const.py

* Update config.py

* Update addons.py

* Update apparmor.py

* Add support for host AppArmor

* Update apparmor.py

* Update apparmor.py

* Update apparmor.py

* Update apparmor.py

* Update apparmor.py

* Update addon.py

* Update apparmor.py

* Update addon.py

* Update addon.py

* Update addon.py

* Update addon.py

* Update const.py

* Update supervisor.py

* Update supervisor.py

* Update supervisor.py

* Add snapshot support

* some cleanup

* Cleanup v2

* Update aiohttp

* fix lint

* fix bugs

* Add info logs
2018-06-20 00:09:18 +02:00
Pascal Vizeli
561e80c2be Extend Systemd Support / Apparmor (#506)
* Update systemd.py

* Update control.py

* Update control.py

* Create service.py

* Update info.py

* Rename hassio/host/asound.tmpl to hassio/host/data/asound.tmpl

* Rename hassio/host/audiodb.json to hassio/host/data/audiodb.json

* Update alsa.py

* Update alsa.py

* Update control.py

* Fix

* Enable call

* fix

* fix args

* Fix gdbus

* parse service data

* Change handling

* Fix states

* Fix parser for tuples

* Fix parser v2

* Fix tuple handling

* Fix regex string handling

* Faster tuple finder

* fix empty detector

* wrong order

* Finish

* fix lint

* better filtering

* fix match

* Fix mode string
2018-06-17 02:07:12 +02:00
Pascal Vizeli
96f47a4c32 Bump version to 108 2018-06-16 01:20:10 +02:00
Pascal Vizeli
7482d6dd45 Update last panel version (#508) 2018-06-15 22:55:57 +02:00
Pascal Vizeli
aea31ee6dd Support host tokens (#507)
* Update coresys.py

* Update bootstrap.py

* Update security.py

* fix lint

* Update bootstrap.py
2018-06-15 22:10:51 +02:00
Pascal Vizeli
de43965ecb Merge pull request #503 from home-assistant/dev
Release 107
2018-06-09 00:01:11 +02:00
Pascal Vizeli
baa61c6aa0 Bump version to 107 2018-06-08 23:48:43 +02:00
Pascal Vizeli
cb22dafb3c Fix bug with 0.70 (#502)
Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>
2018-06-08 23:47:59 +02:00
Pascal Vizeli
ea26784c3e Merge pull request #501 from home-assistant/dev
Release 106
2018-06-08 22:14:05 +02:00
Pascal Vizeli
72332ed40f New panel for 0.71 and 0.72 (#500)
Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>
2018-06-08 21:52:06 +02:00
Pascal Vizeli
46f2bf16a8 Bump version to 106 2018-06-08 21:51:08 +02:00
Ville Skyttä
e2725f8033 Spelling and grammar fixes (#499) 2018-06-08 21:32:06 +02:00
Pascal Vizeli
9084ac119f Fix version conflict 2018-05-29 19:40:16 +02:00
Pascal Vizeli
41943ba61a Delete .gitattributes 2018-05-29 19:38:00 +02:00
Pascal Vizeli
33794669a1 Last version.json update 2018-05-29 19:36:01 +02:00
Pascal Vizeli
fe155a4ff0 Read version from AWS (#488)
* Read version from AWS

* Update const.py

* Update updater.py

* Update updater.py

* Update updater.py

* Update updater.py

* Update updater.py

* Update const.py

* Update updater.py
2018-05-29 19:14:09 +02:00
Pascal Vizeli
124e487ef7 Support new panel generation (#487)
* Support new panel generation

* fix lint
2018-05-29 17:53:09 +02:00
Pascal Vizeli
f361916a60 Update docker timeout to 900sec (#486) 2018-05-29 17:37:20 +02:00
Pascal Vizeli
20afa1544b Bump version to 105 2018-05-29 00:22:12 +02:00
Pascal Vizeli
c08d5af4db Fix version conflicts 2018-05-29 00:21:24 +02:00
Pascal Vizeli
dc341c8af8 Fix version conflicts 2018-05-29 00:18:08 +02:00
Pascal Vizeli
2507b52adb Update Home Assistant to 0.70.0 2018-05-28 23:59:12 +02:00
Pascal Vizeli
1302708135 Update Home Assistant to 0.70.0 2018-05-28 23:58:45 +02:00
Pascal Vizeli
1314812f92 Update Home Assistant to 0.70.0 2018-05-28 23:53:28 +02:00
Pascal Vizeli
f739e3ed11 Update Hass.io to 104 2018-05-28 23:30:48 +02:00
Pascal Vizeli
abb526fc0f Update Panel / fix icons (#483) 2018-05-28 23:29:34 +02:00
Pascal Vizeli
efb1a24b8f Expose panel update (#482)
* Update __init__.py

* Update setup.py

* Update security.py

* Update setup.py

* Update __init__.py

* Update setup.py

* Update __init__.py
2018-05-28 23:16:03 +02:00
Pascal Vizeli
bc0835963d Bump version to 104 2018-05-28 21:28:19 +02:00
Pascal Vizeli
316190dff8 Fix new panel build for 0.70.0 (#481)
Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>
2018-05-28 21:24:17 +02:00
Pascal Vizeli
029ead0c7c Home Assistant 0.70.0b7 2018-05-27 10:52:10 +02:00
Paulus Schoutsen
a85172f30b Update to b7 2018-05-26 22:03:24 -04:00
Pascal Vizeli
dfe2532813 0.70.0b5 2018-05-26 22:28:47 +02:00
Pascal Vizeli
cf3bb23629 Home Assistant 0.70.0b5 2018-05-26 22:28:31 +02:00
Pascal Vizeli
2132042aca Update Home Assistant to version 0.70.0b3 2018-05-25 19:27:49 +02:00
Pascal Vizeli
19e448fc54 Update Home Assistant to version 0.70.0b3 2018-05-25 19:27:33 +02:00
c727
a4e0fb8e99 Update HA beta to 0.70.0b2 2018-05-22 15:03:18 +02:00
Paulus Schoutsen
5b72e2887e Update Hass.io to 0.70.0b2 2018-05-21 21:14:41 -04:00
Pascal Vizeli
d2b6ec1b7e Update Home Assistant to version 0.70.0b1 2018-05-21 15:38:04 +02:00
Paulus Schoutsen
4b541a23c4 Update Hass.io to 0.70.0b1 2018-05-21 09:27:11 -04:00
Pascal Vizeli
99869449ae Update Home Assistant to 0.70.0b0 2018-05-19 10:21:23 +02:00
Pascal Vizeli
eab73f3895 Update Home Assistant to 0.70.0b0 2018-05-19 10:20:55 +02:00
Pascal Vizeli
9e96615ffa Update Home Assistant to version 0.69.1 2018-05-13 10:20:56 +02:00
Pascal Vizeli
350010feb5 Update Home Assistant to version 0.69.1 2018-05-13 10:20:38 +02:00
Pascal Vizeli
7395e4620b Update Home Assistant to version 0.69.1 2018-05-13 10:20:18 +02:00
Pascal Vizeli
7d91ae4513 Update Home Assistant to 0.69.0 2018-05-11 22:32:38 +02:00
Pascal Vizeli
343f759983 Update Home Assistant to 0.69.0 2018-05-11 22:32:01 +02:00
Pascal Vizeli
24ee3f8cc0 Update Home Assistant to 0.69.0 2018-05-11 22:31:41 +02:00
Pascal Vizeli
c143eadb62 Update Home-Assistant 2018-05-09 20:31:22 +02:00
Pascal Vizeli
f185eece8a Update Hass.io and Home Assistant 2018-05-09 13:15:17 +02:00
Pascal Vizeli
9d951280ef Update const.py 2018-05-09 11:07:07 +02:00
Pascal Vizeli
3f598bafc0 Bugfix panel loading (#464) 2018-05-09 11:06:19 +02:00
Franck Nijhof
cddd859f56 🔈 Improves of audio devices handling (#463) 2018-05-08 14:27:17 +02:00
Pascal Vizeli
ac437f809a Update Home Assistant 0.69.0b2 2018-05-07 23:53:54 +02:00
Pascal Vizeli
1fafed5a07 Update Home Assistant and Hass.io 2018-05-06 09:52:55 +02:00
Pascal Vizeli
7adb81b350 Update const.py 2018-05-06 09:45:46 +02:00
Pascal Vizeli
4647035b00 Bugfix Websession 2018-05-06 09:44:58 +02:00
Pascal Vizeli
7f84073b12 Update Hass.io to version 103.1 2018-05-05 23:29:24 +02:00
Pascal Vizeli
e383a11bb7 Pump version to fix 2018-05-05 23:19:56 +02:00
72 changed files with 1730 additions and 309 deletions

2
.gitattributes vendored
View File

@@ -1,2 +0,0 @@
# Ignore version on merge
version.json merge=ours

62
API.md
View File

@@ -227,14 +227,12 @@ return:
```json
{
"hostname": "hostname|null",
"features": ["shutdown", "reboot", "update", "hostname"],
"operating_system": "Hass.io-OS XY|Ubuntu 16.4|null",
"features": ["shutdown", "reboot", "hostname", "services", "hassos"],
"operating_system": "HassOS XY|Ubuntu 16.4|null",
"kernel": "4.15.7|null",
"chassis": "specific|null",
"type": "Hass.io-OS Type|null",
"deployment": "stable|beta|dev|null",
"version": "xy|null",
"last_version": "xy|null",
"cpe": "xy|null",
}
```
@@ -246,18 +244,50 @@ return:
}
```
- POST `/host/reload`
- POST `/host/update`
Optional:
#### Services
- GET `/host/services`
```json
{
"version": "VERSION"
"services": [
{
"name": "xy.service",
"description": "XY ...",
"state": "active|"
}
]
}
```
- POST `/host/reload`
- POST `/host/service/{unit}/stop`
- POST `/host/service/{unit}/start`
- POST `/host/service/{unit}/reload`
### HassOS
- GET `/hassos/info`
```json
{
"version": "2.3",
"version_latest": "2.4",
"board": "ova|rpi"
}
```
- POST `/hassos/update`
```json
{
"version": "optional"
}
```
- POST `/hassos/config/sync`
Load host configs from a USB stick.
### Hardware
@@ -430,7 +460,6 @@ Get all available addons.
"host_ipc": "bool",
"host_dbus": "bool",
"privileged": ["NET_ADMIN", "SYS_ADMIN"],
"seccomp": "disable|default|profile",
"apparmor": "disable|default|profile",
"devices": ["/dev/xy"],
"auto_uart": "bool",
@@ -442,6 +471,7 @@ Get all available addons.
"stdin": "bool",
"webui": "null|http(s)://[HOST]:port/xy/zx",
"gpio": "bool",
"devicetree": "bool",
"audio": "bool",
"audio_input": "null|0,0",
"audio_output": "null|0,0",
@@ -569,17 +599,9 @@ return:
}
```
- GET `/services/xy`
```json
{
"available": "bool",
"xy": {}
}
```
#### MQTT
This service perform a auto discovery to Home-Assistant.
This service performs an auto discovery to Home-Assistant.
- GET `/services/mqtt`
```json

View File

@@ -17,7 +17,7 @@ RUN apk add --no-cache \
python3-dev \
g++ \
&& pip3 install --no-cache-dir \
uvloop==0.9.1 \
uvloop==0.10.1 \
cchardet==2.1.1 \
pycryptodome==3.4.11 \
&& apk del .build-dependencies

View File

@@ -40,11 +40,11 @@ class AddonManager(CoreSysAttributes):
return list(self.repositories_obj.values())
def get(self, addon_slug):
"""Return a add-on from slug."""
"""Return an add-on from slug."""
return self.addons_obj.get(addon_slug)
def from_uuid(self, uuid):
"""Return a add-on from uuid."""
"""Return an add-on from uuid."""
for addon in self.list_addons:
if addon.is_installed and uuid == addon.uuid:
return addon

View File

@@ -25,11 +25,13 @@ from ..const import (
ATTR_HASSIO_API, ATTR_AUDIO, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT,
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY, ATTR_HOST_IPC,
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_DISCOVERY, ATTR_SERVICES,
ATTR_SECCOMP, ATTR_APPARMOR, SECURITY_PROFILE, SECURITY_DISABLE,
ATTR_APPARMOR, ATTR_DEVICETREE, SECURITY_PROFILE, SECURITY_DISABLE,
SECURITY_DEFAULT)
from ..coresys import CoreSysAttributes
from ..docker.addon import DockerAddon
from ..utils.json import write_json_file, read_json_file
from ..utils.apparmor import adjust_profile
from ..exceptions import HostAppArmorError
_LOGGER = logging.getLogger(__name__)
@@ -70,7 +72,7 @@ class Addon(CoreSysAttributes):
@property
def is_installed(self):
"""Return True if a addon is installed."""
"""Return True if an addon is installed."""
return self._id in self._data.system
@property
@@ -164,7 +166,7 @@ class Addon(CoreSysAttributes):
@property
def uuid(self):
"""Return a API token for this add-on."""
"""Return an API token for this add-on."""
if self.is_installed:
return self._data.user[self._id][ATTR_UUID]
return None
@@ -319,21 +321,12 @@ class Addon(CoreSysAttributes):
"""Return list of privilege."""
return self._mesh.get(ATTR_PRIVILEGED)
@property
def seccomp(self):
"""Return True if seccomp is enabled."""
if not self._mesh.get(ATTR_SECCOMP):
return SECURITY_DISABLE
elif self.path_seccomp.exists():
return SECURITY_PROFILE
return SECURITY_DEFAULT
@property
def apparmor(self):
"""Return True if seccomp is enabled."""
"""Return True if apparmor is enabled."""
if not self._mesh.get(ATTR_APPARMOR):
return SECURITY_DISABLE
elif self.path_apparmor.exists():
elif self.sys_host.apparmor.exists(self.slug):
return SECURITY_PROFILE
return SECURITY_DEFAULT
@@ -362,6 +355,11 @@ class Addon(CoreSysAttributes):
"""Return True if the add-on access to gpio interface."""
return self._mesh[ATTR_GPIO]
@property
def with_devicetree(self):
"""Return True if the add-on read access to devicetree."""
return self._mesh[ATTR_DEVICETREE]
@property
def with_audio(self):
"""Return True if the add-on access to audio."""
@@ -411,7 +409,7 @@ class Addon(CoreSysAttributes):
@property
def with_icon(self):
"""Return True if a icon exists."""
"""Return True if an icon exists."""
return self.path_icon.exists()
@property
@@ -493,15 +491,10 @@ class Addon(CoreSysAttributes):
"""Return path to addon changelog."""
return Path(self.path_location, 'CHANGELOG.md')
@property
def path_seccomp(self):
"""Return path to custom seccomp profile."""
return Path(self.path_location, 'seccomp.json')
@property
def path_apparmor(self):
"""Return path to custom AppArmor profile."""
return Path(self.path_location, 'apparmor')
return Path(self.path_location, 'apparmor.txt')
@property
def path_asound(self):
@@ -549,6 +542,27 @@ class Addon(CoreSysAttributes):
return True
async def _install_apparmor(self):
"""Install or Update AppArmor profile for Add-on."""
exists_local = self.sys_host.apparmor.exists(self.slug)
exists_addon = self.path_apparmor.exists()
# Nothing to do
if not exists_local and not exists_addon:
return
# Need removed
if exists_local and not exists_addon:
await self.sys_host.apparmor.remove_profile(self.slug)
return
# Need install/update
with TemporaryDirectory(dir=self.sys_config.path_tmp) as tmp_folder:
profile_file = Path(tmp_folder, 'apparmor.txt')
adjust_profile(self.slug, self.path_apparmor, profile_file)
await self.sys_host.apparmor.load_profile(self.slug, profile_file)
@property
def schema(self):
"""Create a schema for addon options."""
@@ -589,7 +603,7 @@ class Addon(CoreSysAttributes):
return True
async def install(self):
"""Install a addon."""
"""Install an addon."""
if self.sys_arch not in self.supported_arch:
_LOGGER.error(
"Addon %s not supported on %s", self._id, self.sys_arch)
@@ -604,6 +618,9 @@ class Addon(CoreSysAttributes):
"Create Home-Assistant addon data folder %s", self.path_data)
self.path_data.mkdir()
# Setup/Fix AppArmor profile
await self._install_apparmor()
if not await self.instance.install(self.last_version):
return False
@@ -612,7 +629,7 @@ class Addon(CoreSysAttributes):
@check_installed
async def uninstall(self):
"""Remove a addon."""
"""Remove an addon."""
if not await self.instance.remove():
return False
@@ -626,6 +643,11 @@ class Addon(CoreSysAttributes):
with suppress(OSError):
self.path_asound.unlink()
# Cleanup apparmor profile
if self.sys_host.apparmor.exists(self.slug):
with suppress(HostAppArmorError):
await self.sys_host.apparmor.remove_profile(self.slug)
self._set_uninstall()
return True
@@ -672,6 +694,9 @@ class Addon(CoreSysAttributes):
return False
self._set_update(self.last_version)
# Setup/Fix AppArmor profile
await self._install_apparmor()
# restore state
if last_state == STATE_STARTED:
await self.start()
@@ -734,11 +759,11 @@ class Addon(CoreSysAttributes):
@check_installed
async def snapshot(self, tar_file):
"""Snapshot a state of a addon."""
"""Snapshot state of an addon."""
with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp:
# store local image
if self.need_build and not await \
self.instance.export_image(Path(temp, "image.tar")):
self.instance.export_image(Path(temp, 'image.tar')):
return False
data = {
@@ -750,11 +775,20 @@ class Addon(CoreSysAttributes):
# store local configs/state
try:
write_json_file(Path(temp, "addon.json"), data)
write_json_file(Path(temp, 'addon.json'), data)
except (OSError, json.JSONDecodeError) as err:
_LOGGER.error("Can't save meta for %s: %s", self._id, err)
return False
# Store AppArmor Profile
if self.sys_host.apparmor.exists(self.slug):
profile = Path(temp, 'apparmor.txt')
try:
self.sys_host.apparmor.backup_profile(self.slug, profile)
except HostAppArmorError:
_LOGGER.error("Can't backup AppArmor profile")
return False
# write into tarfile
def _write_tarfile():
"""Write tar inside loop."""
@@ -773,7 +807,7 @@ class Addon(CoreSysAttributes):
return True
async def restore(self, tar_file):
"""Restore a state of a addon."""
"""Restore state of an addon."""
with TemporaryDirectory(dir=str(self.sys_config.path_tmp)) as temp:
# extract snapshot
def _extract_tarfile():
@@ -789,7 +823,7 @@ class Addon(CoreSysAttributes):
# read snapshot data
try:
data = read_json_file(Path(temp, "addon.json"))
data = read_json_file(Path(temp, 'addon.json'))
except (OSError, json.JSONDecodeError) as err:
_LOGGER.error("Can't read addon.json: %s", err)
@@ -810,7 +844,7 @@ class Addon(CoreSysAttributes):
if not await self.instance.exists():
_LOGGER.info("Restore image for addon %s", self._id)
image_file = Path(temp, "image.tar")
image_file = Path(temp, 'image.tar')
if image_file.is_file():
await self.instance.import_image(image_file, version)
else:
@@ -833,6 +867,16 @@ class Addon(CoreSysAttributes):
_LOGGER.error("Can't restore origin data: %s", err)
return False
# Restore AppArmor
profile_file = Path(temp, 'apparmor.txt')
if profile_file.exists():
try:
await self.sys_host.apparmor.load_profile(
self.slug, profile_file)
except HostAppArmorError:
_LOGGER.error("Can't restore AppArmor profile")
return False
# run addon
if data[ATTR_STATE] == STATE_STARTED:
return await self.start()

View File

@@ -18,7 +18,7 @@ from ..const import (
ATTR_AUDIO_OUTPUT, ATTR_HASSIO_API, ATTR_BUILD_FROM, ATTR_SQUASH,
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY,
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_SERVICES, ATTR_DISCOVERY,
ATTR_SECCOMP, ATTR_APPARMOR)
ATTR_APPARMOR, ATTR_DEVICETREE)
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE
_LOGGER = logging.getLogger(__name__)
@@ -108,10 +108,10 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Optional(ATTR_MAP, default=list): [vol.Match(RE_VOLUME)],
vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): vol.Coerce(str)},
vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGED_ALL)],
vol.Optional(ATTR_SECCOMP, default=True): vol.Boolean(),
vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(),
vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(),
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
vol.Optional(ATTR_STDIN, default=False): vol.Boolean(),

View File

@@ -9,6 +9,7 @@ from .discovery import APIDiscovery
from .homeassistant import APIHomeAssistant
from .hardware import APIHardware
from .host import APIHost
from .hassos import APIHassOS
from .proxy import APIProxy
from .supervisor import APISupervisor
from .snapshots import APISnapshots
@@ -30,13 +31,14 @@ class RestAPI(CoreSysAttributes):
middlewares=[self.security.token_validation], loop=coresys.loop)
# service stuff
self._handler = None
self.server = None
self._runner = web.AppRunner(self.webapp)
self._site = None
async def load(self):
"""Register REST API Calls."""
self._register_supervisor()
self._register_host()
self._register_hassos()
self._register_hardware()
self._register_homeassistant()
self._register_proxy()
@@ -55,8 +57,24 @@ class RestAPI(CoreSysAttributes):
web.get('/host/info', api_host.info),
web.post('/host/reboot', api_host.reboot),
web.post('/host/shutdown', api_host.shutdown),
web.post('/host/update', api_host.update),
web.post('/host/reload', api_host.reload),
web.get('/host/services', api_host.services),
web.post('/host/services/{service}/stop', api_host.service_stop),
web.post('/host/services/{service}/start', api_host.service_start),
web.post(
'/host/services/{service}/restart', api_host.service_restart),
web.post(
'/host/services/{service}/reload', api_host.service_reload),
])
def _register_hassos(self):
"""Register hassos function."""
api_hassos = APIHassOS()
api_hassos.coresys = self.coresys
self.webapp.add_routes([
web.get('/hassos/info', api_hassos.info),
web.post('/hassos/config/sync', api_hassos.config_sync),
])
def _register_hardware(self):
@@ -185,10 +203,11 @@ class RestAPI(CoreSysAttributes):
def _register_panel(self):
"""Register panel for homeassistant."""
def create_response(build_type):
panel_dir = Path(__file__).parent.joinpath("panel")
def create_response(panel_file):
"""Create a function to generate a response."""
path = Path(__file__).parent.joinpath(
f"panel/{build_type}.html")
path = panel_dir.joinpath(f"{panel_file!s}.html")
return lambda request: web.FileResponse(path)
# This route is for backwards compatibility with HA < 0.58
@@ -201,30 +220,27 @@ class RestAPI(CoreSysAttributes):
web.get('/panel_latest', create_response('hassio-main-latest')),
])
# This route is for HA > 0.61
# This route is for backwards compatibility with HA 0.62 - 0.70
self.webapp.add_routes([
web.get('/app-es5/index.html', create_response('index')),
web.get('/app-es5/hassio-app.html', create_response('hassio-app')),
])
# This route is for HA > 0.70
self.webapp.add_routes([web.static('/app', panel_dir)])
async def start(self):
"""Run rest api webserver."""
self._handler = self.webapp.make_handler()
await self._runner.setup()
self._site = web.TCPSite(self._runner, "0.0.0.0", 80)
try:
self.server = await self.sys_loop.create_server(
self._handler, "0.0.0.0", "80")
await self._site.start()
except OSError as err:
_LOGGER.fatal(
"Failed to create HTTP server at 0.0.0.0:80 -> %s", err)
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.shutdown(60)
await self.webapp.cleanup()
await self._site.stop()
await self._runner.cleanup()

View File

@@ -17,7 +17,7 @@ from ..const import (
ATTR_CHANGELOG, ATTR_HOST_IPC, ATTR_HOST_DBUS, ATTR_LONG_DESCRIPTION,
ATTR_CPU_PERCENT, ATTR_MEMORY_LIMIT, ATTR_MEMORY_USAGE, ATTR_NETWORK_TX,
ATTR_NETWORK_RX, ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_ICON, ATTR_SERVICES,
ATTR_DISCOVERY, ATTR_SECCOMP, ATTR_APPARMOR,
ATTR_DISCOVERY, ATTR_APPARMOR, ATTR_DEVICETREE,
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT)
from ..coresys import CoreSysAttributes
from ..validate import DOCKER_PORTS, ALSA_DEVICE
@@ -42,10 +42,10 @@ class APIAddons(CoreSysAttributes):
"""Handle rest api for addons functions."""
def _extract_addon(self, request, check_installed=True):
"""Return addon and if not exists trow a exception."""
"""Return addon, throw an exception it it doesn't exist."""
addon = self.sys_addons.get(request.match_info.get('addon'))
if not addon:
raise RuntimeError("Addon not exists")
raise RuntimeError("Addon does not exist")
if check_installed and not addon.is_installed:
raise RuntimeError("Addon is not installed")
@@ -126,7 +126,6 @@ class APIAddons(CoreSysAttributes):
ATTR_HOST_IPC: addon.host_ipc,
ATTR_HOST_DBUS: addon.host_dbus,
ATTR_PRIVILEGED: addon.privileged,
ATTR_SECCOMP: addon.seccomp,
ATTR_APPARMOR: addon.apparmor,
ATTR_DEVICES: self._pretty_devices(addon),
ATTR_ICON: addon.with_icon,
@@ -137,6 +136,7 @@ class APIAddons(CoreSysAttributes):
ATTR_HASSIO_API: addon.access_hassio_api,
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
ATTR_GPIO: addon.with_gpio,
ATTR_DEVICETREE: addon.with_devicetree,
ATTR_AUDIO: addon.with_audio,
ATTR_AUDIO_INPUT: addon.audio_input,
ATTR_AUDIO_OUTPUT: addon.audio_output,

33
hassio/api/hassos.py Normal file
View File

@@ -0,0 +1,33 @@
"""Init file for Hass.io hassos rest api."""
import asyncio
import logging
import voluptuous as vol
from .utils import api_process
from ..const import ATTR_VERSION, ATTR_BOARD, ATTR_VERSION_LATEST
from ..coresys import CoreSysAttributes
_LOGGER = logging.getLogger(__name__)
SCHEMA_VERSION = vol.Schema({
vol.Optional(ATTR_VERSION): vol.Coerce(str),
})
class APIHassOS(CoreSysAttributes):
"""Handle rest api for hassos functions."""
@api_process
async def info(self, request):
"""Return hassos information."""
return {
ATTR_VERSION: self.sys_hassos.version,
ATTR_VERSION_LATEST: self.sys_hassos.version,
ATTR_BOARD: self.sys_hassos.board,
}
@api_process
def config_sync(self, request):
"""Trigger config reload on HassOS."""
return asyncio.shield(self.sys_hassos.config_sync())

View File

@@ -6,15 +6,14 @@ import voluptuous as vol
from .utils import api_process, api_validate
from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_HOSTNAME, ATTR_FEATURES, ATTR_KERNEL,
ATTR_TYPE, ATTR_OPERATING_SYSTEM, ATTR_CHASSIS, ATTR_DEPLOYMENT)
ATTR_HOSTNAME, ATTR_FEATURES, ATTR_KERNEL, ATTR_OPERATING_SYSTEM,
ATTR_CHASSIS, ATTR_DEPLOYMENT, ATTR_STATE, ATTR_NAME, ATTR_DESCRIPTON,
ATTR_SERVICES, ATTR_CPE)
from ..coresys import CoreSysAttributes
_LOGGER = logging.getLogger(__name__)
SCHEMA_VERSION = vol.Schema({
vol.Optional(ATTR_VERSION): vol.Coerce(str),
})
SERVICE = 'service'
SCHEMA_OPTIONS = vol.Schema({
vol.Optional(ATTR_HOSTNAME): vol.Coerce(str),
@@ -29,9 +28,7 @@ class APIHost(CoreSysAttributes):
"""Return host information."""
return {
ATTR_CHASSIS: self.sys_host.info.chassis,
ATTR_VERSION: None,
ATTR_LAST_VERSION: None,
ATTR_TYPE: None,
ATTR_CPE: self.sys_host.info.cpe,
ATTR_FEATURES: self.sys_host.supperted_features,
ATTR_HOSTNAME: self.sys_host.info.hostname,
ATTR_OPERATING_SYSTEM: self.sys_host.info.operating_system,
@@ -65,8 +62,40 @@ class APIHost(CoreSysAttributes):
return asyncio.shield(self.sys_host.reload())
@api_process
async def update(self, request):
"""Update host OS."""
pass
# body = await api_validate(SCHEMA_VERSION, request)
# version = body.get(ATTR_VERSION, self.sys_host.last_version)
async def services(self, request):
"""Return list of available services."""
services = []
for unit in self.sys_host.services:
services.append({
ATTR_NAME: unit.name,
ATTR_DESCRIPTON: unit.description,
ATTR_STATE: unit.state,
})
return {
ATTR_SERVICES: services
}
@api_process
def service_start(self, request):
"""Start a service."""
unit = request.match_info.get(SERVICE)
return asyncio.shield(self.sys_host.services.start(unit))
@api_process
def service_stop(self, request):
"""Stop a service."""
unit = request.match_info.get(SERVICE)
return asyncio.shield(self.sys_host.services.stop(unit))
@api_process
def service_reload(self, request):
"""Reload a service."""
unit = request.match_info.get(SERVICE)
return asyncio.shield(self.sys_host.services.reload(unit))
@api_process
def service_restart(self, request):
"""Restart a service."""
unit = request.match_info.get(SERVICE)
return asyncio.shield(self.sys_host.services.restart(unit))

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,419 @@
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{104:function(n,r,t){"use strict";t.r(r),t.d(r,"marked",function(){return a}),t.d(r,"filterXSS",function(){return c});var e=t(99),i=t.n(e),o=t(97),u=t.n(o),a=i.a,c=u.a}}]);

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,389 @@
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1 @@
!function(e){function n(n){for(var t,o,i=n[0],u=n[1],a=0,c=[];a<i.length;a++)o=i[a],r[o]&&c.push(r[o][0]),r[o]=0;for(t in u)Object.prototype.hasOwnProperty.call(u,t)&&(e[t]=u[t]);for(f&&f(n);c.length;)c.shift()()}var t={},r={6:0};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.e=function(e){var n=[],t=r[e];if(0!==t)if(t)n.push(t[2]);else{var i=new Promise(function(n,o){t=r[e]=[n,o]});n.push(t[2]=i);var u,a=document.getElementsByTagName("head")[0],f=document.createElement("script");f.charset="utf-8",f.timeout=120,o.nc&&f.setAttribute("nonce",o.nc),f.src=function(e){return o.p+"chunk."+{0:"f3880aa331d3ef2ddf32",1:"a8e86d80be46b3b6e16d",2:"2cdff35c6685a5344cd2",3:"ff92199b0d422767d108",4:"c77b56beea1d4547ff5f",5:"c93f37c558ff32991708"}[e]+".js"}(e),u=function(n){f.onerror=f.onload=null,clearTimeout(c);var t=r[e];if(0!==t){if(t){var o=n&&("load"===n.type?"missing":n.type),i=n&&n.target&&n.target.src,u=new Error("Loading chunk "+e+" failed.\n("+o+": "+i+")");u.type=o,u.request=i,t[1](u)}r[e]=void 0}};var c=setTimeout(function(){u({type:"timeout",target:f})},12e4);f.onerror=f.onload=u,a.appendChild(f)}return Promise.all(n)},o.m=e,o.c=t,o.d=function(e,n,t){o.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,n){if(1&n&&(e=o(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(o.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)o.d(t,r,function(n){return e[n]}.bind(null,r));return t},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="/api/hassio/app/",o.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],u=i.push.bind(i);i.push=n,i=i.slice();for(var a=0;a<i.length;a++)n(i[a]);var f=u;o(o.s=0)}([function(e,n,t){window.loadES5Adapter().then(function(){Promise.all([t.e(0),t.e(3)]).then(t.bind(null,1)),Promise.all([t.e(0),t.e(1),t.e(2)]).then(t.bind(null,2))})}]);

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -11,27 +11,28 @@
padding: 0;
}
</style>
<script src='/frontend_es5/custom-elements-es5-adapter.js'></script>
</head>
<body>
<hassio-app></hassio-app>
<script>
function addScript(src) {
var e = document.createElement('script');
e.src = src;
document.head.appendChild(e);
}
if (!window.parent.HASS_DEV) {
addScript('/frontend_es5/custom-elements-es5-adapter.js');
}
var webComponentsSupported = (
'customElements' in window &&
'import' in document.createElement('link') &&
'content' in document.createElement('template'));
if (!webComponentsSupported) {
addScript('/static/webcomponents-lite.js');
}
function addScript(src) {
var e = document.createElement('script');
e.src = src;
document.write(e.outerHTML);
}
var webComponentsSupported = (
'customElements' in window &&
'import' in document.createElement('link') &&
'content' in document.createElement('template'));
if (!webComponentsSupported) {
addScript('/static/webcomponents-lite.js');
}
</script>
<!--
Disabled while we make Home Assistant able to serve the right files.
<script src="./app.js"></script>
-->
<link rel='import' href='./hassio-app.html'>
<link rel='import' href='/static/mdi.html' async>
</body>
</html>

Binary file not shown.

View File

@@ -34,7 +34,7 @@ class APIProxy(CoreSysAttributes):
try:
data = None
headers = {}
method = getattr(self._websession_ssl, request.method.lower())
method = getattr(self.sys_websession_ssl, request.method.lower())
params = request.query or None
# read data

View File

@@ -35,23 +35,27 @@ class SecurityMiddleware(CoreSysAttributes):
_LOGGER.debug("Passthrough %s", request.path)
return await handler(request)
# Need to be removed later
# Unknown API access
if not hassio_token:
_LOGGER.warning("Invalid token for access %s", request.path)
request[REQUEST_FROM] = 'UNKNOWN'
return await handler(request)
raise HTTPUnauthorized()
# Home-Assistant
if hassio_token == self.sys_homeassistant.uuid:
_LOGGER.debug("%s access from Home-Assistant", request.path)
request[REQUEST_FROM] = 'homeassistant'
return await handler(request)
# Host
if hassio_token == self.sys_machine_id:
_LOGGER.debug("%s access from Host", request.path)
request[REQUEST_FROM] = 'host'
# Add-on
addon = self.sys_addons.from_uuid(hassio_token)
if addon:
_LOGGER.info("%s access from %s", request.path, addon.slug)
request[REQUEST_FROM] = addon.slug
return await handler(request)
raise HTTPUnauthorized()
if not request.get(REQUEST_FROM):
raise HTTPUnauthorized()
return await handler(request)

View File

@@ -10,10 +10,10 @@ class APIServices(CoreSysAttributes):
"""Handle rest api for services functions."""
def _extract_service(self, request):
"""Return service and if not exists trow a exception."""
"""Return service, throw an exception if it doesn't exist."""
service = self.sys_services.get(request.match_info.get('service'))
if not service:
raise RuntimeError("Service not exists")
raise RuntimeError("Service does not exist")
return service

View File

@@ -49,10 +49,10 @@ class APISnapshots(CoreSysAttributes):
"""Handle rest api for snapshot functions."""
def _extract_snapshot(self, request):
"""Return addon and if not exists trow a exception."""
"""Return snapshot, throw an exception if it doesn't exist."""
snapshot = self.sys_snapshots.get(request.match_info.get('snapshot'))
if not snapshot:
raise RuntimeError("Snapshot not exists")
raise RuntimeError("Snapshot does not exist")
return snapshot
@api_process

View File

@@ -119,7 +119,7 @@ class APISupervisor(CoreSysAttributes):
@api_process
async def reload(self, request):
"""Reload addons, config ect."""
"""Reload addons, config etc."""
tasks = [
self.sys_updater.reload(),
]

View File

@@ -71,7 +71,7 @@ def api_process_raw(content):
def api_return_error(message=None):
"""Return a API error message."""
"""Return an API error message."""
return web.json_response({
JSON_RESULT: RESULT_ERROR,
JSON_MESSAGE: message,
@@ -79,7 +79,7 @@ def api_return_error(message=None):
def api_return_ok(data=None):
"""Return a API ok answer."""
"""Return an API ok answer."""
return web.json_response({
JSON_RESULT: RESULT_OK,
JSON_DATA: data or {},

View File

@@ -21,9 +21,15 @@ from .services import ServiceManager
from .services import Discovery
from .host import HostManager
from .dbus import DBusManager
from .hassos import HassOS
_LOGGER = logging.getLogger(__name__)
ENV_SHARE = 'SUPERVISOR_SHARE'
ENV_NAME = 'SUPERVISOR_NAME'
ENV_REPO = 'HOMEASSISTANT_REPOSITORY'
ENV_MACHINE = 'MACHINE_ID'
def initialize_coresys(loop):
"""Initialize HassIO coresys/objects."""
@@ -42,10 +48,14 @@ def initialize_coresys(loop):
coresys.services = ServiceManager(coresys)
coresys.discovery = Discovery(coresys)
coresys.dbus = DBusManager(coresys)
coresys.hassos = HassOS(coresys)
# bootstrap config
initialize_system_data(coresys)
# Set Machine/Host ID
coresys.machine_id = os.environ.get(ENV_MACHINE)
return coresys
@@ -95,6 +105,11 @@ def initialize_system_data(coresys):
_LOGGER.info("Create hassio share folder %s", config.path_share)
config.path_share.mkdir()
# apparmor folder
if not config.path_apparmor.is_dir():
_LOGGER.info("Create hassio apparmor folder %s", config.path_apparmor)
config.path_apparmor.mkdir()
return config
@@ -139,8 +154,7 @@ def initialize_logging():
def check_environment():
"""Check if all environment are exists."""
# check environment variables
for key in ('SUPERVISOR_SHARE', 'SUPERVISOR_NAME',
'HOMEASSISTANT_REPOSITORY'):
for key in (ENV_SHARE, ENV_NAME, ENV_REPO):
try:
os.environ[key]
except KeyError:

View File

@@ -25,6 +25,7 @@ ADDONS_DATA = PurePath("addons/data")
BACKUP_DATA = PurePath("backup")
SHARE_DATA = PurePath("share")
TMP_DATA = PurePath("tmp")
APPARMOR_DATA = PurePath("apparmor")
DEFAULT_BOOT_TIME = datetime.utcfromtimestamp(0).isoformat()
@@ -156,6 +157,11 @@ class CoreConfig(JsonConfig):
"""Return root share data folder."""
return Path(HASSIO_DATA, SHARE_DATA)
@property
def path_apparmor(self):
"""Return root apparmor profile folder."""
return Path(HASSIO_DATA, APPARMOR_DATA)
@property
def path_extern_share(self):
"""Return root share data folder extern for docker."""

View File

@@ -2,12 +2,13 @@
from pathlib import Path
from ipaddress import ip_network
HASSIO_VERSION = '103'
HASSIO_VERSION = '109'
URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/'
'hassio/{}/version.json')
URL_HASSIO_ADDONS = 'https://github.com/home-assistant/hassio-addons'
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
URL_HASSIO_VERSION = \
"https://s3.amazonaws.com/hassio-version/{channel}.json"
URL_HASSIO_APPARMOR = \
"https://s3.amazonaws.com/hassio-version/apparmor.txt"
HASSIO_DATA = Path("/data")
@@ -70,6 +71,7 @@ ATTR_SOURCE = 'source'
ATTR_FEATURES = 'features'
ATTR_ADDONS = 'addons'
ATTR_VERSION = 'version'
ATTR_VERSION_LATEST = 'version_latest'
ATTR_AUTO_UART = 'auto_uart'
ATTR_LAST_BOOT = 'last_boot'
ATTR_LAST_VERSION = 'last_version'
@@ -163,8 +165,10 @@ ATTR_PROTECTED = 'protected'
ATTR_CRYPTO = 'crypto'
ATTR_BRANCH = 'branch'
ATTR_KERNEL = 'kernel'
ATTR_SECCOMP = 'seccomp'
ATTR_APPARMOR = 'apparmor'
ATTR_DEVICETREE = 'devicetree'
ATTR_CPE = 'cpe'
ATTR_BOARD = 'board'
SERVICE_MQTT = 'mqtt'
@@ -215,5 +219,6 @@ SECURITY_DISABLE = 'disable'
FEATURES_SHUTDOWN = 'shutdown'
FEATURES_REBOOT = 'reboot'
FEATURES_UPDATE = 'update'
FEATURES_HASSOS = 'hassos'
FEATURES_HOSTNAME = 'hostname'
FEATURES_SERVICES = 'services'

View File

@@ -32,6 +32,9 @@ class HassIO(CoreSysAttributes):
# Load Host
await self.sys_host.load()
# Load HassOS
await self.sys_hassos.load()
# Load Supervisor
await self.sys_supervisor.load()

View File

@@ -17,6 +17,7 @@ class CoreSys:
"""Initialize coresys."""
# Static attributes
self.exit_code = 0
self.machine_id = None
# External objects
self._loop = loop
@@ -42,6 +43,7 @@ class CoreSys:
self._tasks = None
self._host = None
self._dbus = None
self._hassos = None
self._services = None
self._discovery = None
@@ -147,7 +149,7 @@ class CoreSys:
@api.setter
def api(self, value):
"""Set a API object."""
"""Set an API object."""
if self._api:
raise RuntimeError("API already set!")
self._api = value
@@ -248,6 +250,18 @@ class CoreSys:
raise RuntimeError("HostManager already set!")
self._host = value
@property
def hassos(self):
"""Return HassOS object."""
return self._hassos
@hassos.setter
def hassos(self, value):
"""Set a HassOS object."""
if self._hassos:
raise RuntimeError("HassOS already set!")
self._hassos = value
def run_in_executor(self, funct, *args):
"""Wrapper for executor pool."""
return self._loop.run_in_executor(None, funct, *args)
@@ -266,4 +280,4 @@ class CoreSysAttributes:
"""Mapping to coresys."""
if name.startswith("sys_") and hasattr(self.coresys, name[4:]):
return getattr(self.coresys, name[4:])
raise AttributeError()
raise AttributeError(f"Can't resolve {name} on {self}")

View File

@@ -37,3 +37,43 @@ class Systemd(DBusInterface):
Return a coroutine.
"""
return self.dbus.Manager.PowerOff()
@dbus_connected
def start_unit(self, unit, mode):
"""Start a systemd service unit.
Return a coroutine.
"""
return self.dbus.Manager.StartUnit(unit, mode)
@dbus_connected
def stop_unit(self, unit, mode):
"""Stop a systemd service unit.
Return a coroutine.
"""
return self.dbus.Manager.StopUnit(unit, mode)
@dbus_connected
def reload_unit(self, unit, mode):
"""Reload a systemd service unit.
Return a coroutine.
"""
return self.dbus.Manager.ReloadOrRestartUnit(unit, mode)
@dbus_connected
def restart_unit(self, unit, mode):
"""Restart a systemd service unit.
Return a coroutine.
"""
return self.dbus.Manager.RestartUnit(unit, mode)
@dbus_connected
def list_units(self):
"""Return a list of available systemd services.
Return a coroutine.
"""
return self.dbus.Manager.ListUnits()

View File

@@ -24,7 +24,7 @@ class DockerAPI:
"""Initialize docker base wrapper."""
self.docker = docker.DockerClient(
base_url="unix:/{}".format(str(SOCKET_DOCKER)),
version='auto', timeout=300)
version='auto', timeout=900)
self.network = DockerNetwork(self.docker)
@property

View File

@@ -124,18 +124,17 @@ class DockerAddon(DockerInterface):
security = []
# AppArmor
if self.addon.apparmor == SECURITY_DISABLE:
apparmor = self.sys_host.apparmor.available
if not apparmor or self.addon.apparmor == SECURITY_DISABLE:
security.append("apparmor:unconfined")
elif self.addon.apparmor == SECURITY_PROFILE:
security.append(f"apparmor={self.addon.slug}")
# Seccomp
if self.addon.seccomp == SECURITY_DISABLE:
security.append("seccomp=unconfined")
elif self.addon.seccomp == SECURITY_PROFILE:
security.append(f"seccomp={self.addon.path_seccomp}")
# Disable Seccomp / We don't support it official and it
# make troubles on some kind of host systems.
security.append("seccomp=unconfined")
return security or None
return security
@property
def tmpfs(self):
@@ -202,6 +201,8 @@ class DockerAddon(DockerInterface):
}})
# Init other hardware mappings
# GPIO support
if self.addon.with_gpio:
volumes.update({
"/sys/class/gpio": {
@@ -212,6 +213,14 @@ class DockerAddon(DockerInterface):
},
})
# DeviceTree support
if self.addon.with_devicetree:
volumes.update({
"/sys/firmware/devicetree": {
'bind': "/sys/firmware/devicetree", 'mode': 'r'
},
})
# Host dbus system
if self.addon.host_dbus:
volumes.update({

View File

@@ -88,6 +88,9 @@ class DockerHomeAssistant(DockerInterface):
return self.sys_docker.run_command(
self.image,
command,
privileged=True,
init=True,
devices=self.devices,
detach=True,
stdout=True,
stderr=True,
@@ -96,7 +99,7 @@ class DockerHomeAssistant(DockerInterface):
},
volumes={
str(self.sys_config.path_extern_config):
{'bind': '/config', 'mode': 'ro'},
{'bind': '/config', 'mode': 'rw'},
str(self.sys_config.path_extern_ssl):
{'bind': '/ssl', 'mode': 'ro'},
}

View File

@@ -120,7 +120,7 @@ class DockerInterface(CoreSysAttributes):
if container.status != 'running':
return False
# we run on a old image, stop and start it
# we run on an old image, stop and start it
if container.image.id != image.id:
return False

View File

@@ -6,11 +6,6 @@ class HassioError(Exception):
pass
class HassioInternalError(HassioError):
"""Internal Hass.io error they can't handle."""
pass
class HassioNotSupportedError(HassioError):
"""Function is not supported."""
pass
@@ -28,6 +23,15 @@ class HostNotSupportedError(HassioNotSupportedError):
pass
class HostServiceError(HostError):
"""Host service functions fails."""
pass
class HostAppArmorError(HostError):
"""Host apparmor functions fails."""
# utils/gdbus
class DBusError(HassioError):
@@ -47,3 +51,20 @@ class DBusFatalError(DBusError):
class DBusParseError(DBusError):
"""DBus parse error."""
pass
# util/apparmor
class AppArmorError(HostAppArmorError):
"""General AppArmor error."""
pass
class AppArmorFileError(AppArmorError):
"""AppArmor profile file error."""
pass
class AppArmorInvalidError(AppArmorError):
"""AppArmor profile validate error."""
pass

65
hassio/hassos.py Normal file
View File

@@ -0,0 +1,65 @@
"""HassOS support on supervisor."""
import logging
from cpe import CPE
from .coresys import CoreSysAttributes
from .exceptions import HassioNotSupportedError
_LOGGER = logging.getLogger(__name__)
class HassOS(CoreSysAttributes):
"""HassOS interface inside HassIO."""
def __init__(self, coresys):
"""Initialize HassOS handler."""
self.coresys = coresys
self._available = False
self._version = None
self._board = None
@property
def available(self):
"""Return True, if HassOS on host."""
return self._available
@property
def version(self):
"""Return version of HassOS."""
return self._version
@property
def board(self):
"""Return board name."""
return self._board
def _check_host(self):
"""Check if HassOS is availabe."""
if not self.available:
_LOGGER.error("No HassOS availabe")
raise HassioNotSupportedError()
async def load(self):
"""Load HassOS data."""
try:
assert self.sys_host.info.cpe is not None
cpe = CPE(self.sys_host.info.cpe)
assert cpe.get_product()[0] == 'hassos'
except (NotImplementedError, IndexError, AssertionError):
_LOGGER.info("Can't detect HassOS")
return
# Store meta data
self._available = True
self._version = cpe.get_version()[0]
self._board = cpe.get_target_hardware()[0]
_LOGGER.info("Detect HassOS %s on host system", self.version)
def config_sync(self):
"""Trigger a host config reload from usb."""
self._check_host()
_LOGGER.info("Sync config from USB on HassOS.")
return self.sys_host.services.restart('hassos-config.service')

View File

@@ -1,10 +1,19 @@
"""Host function like audio/dbus/systemd."""
from contextlib import suppress
import logging
from .alsa import AlsaAudio
from .apparmor import AppArmorControl
from .control import SystemControl
from .info import InfoCenter
from ..const import FEATURES_REBOOT, FEATURES_SHUTDOWN, FEATURES_HOSTNAME
from .services import ServiceManager
from ..const import (
FEATURES_REBOOT, FEATURES_SHUTDOWN, FEATURES_HOSTNAME, FEATURES_SERVICES,
FEATURES_HASSOS)
from ..coresys import CoreSysAttributes
from ..exceptions import HassioError
_LOGGER = logging.getLogger(__name__)
class HostManager(CoreSysAttributes):
@@ -14,14 +23,21 @@ class HostManager(CoreSysAttributes):
"""Initialize Host manager."""
self.coresys = coresys
self._alsa = AlsaAudio(coresys)
self._apparmor = AppArmorControl(coresys)
self._control = SystemControl(coresys)
self._info = InfoCenter(coresys)
self._services = ServiceManager(coresys)
@property
def alsa(self):
"""Return host ALSA handler."""
return self._alsa
@property
def apparmor(self):
"""Return host apparmor handler."""
return self._apparmor
@property
def control(self):
"""Return host control handler."""
@@ -32,6 +48,11 @@ class HostManager(CoreSysAttributes):
"""Return host info handler."""
return self._info
@property
def services(self):
"""Return host services handler."""
return self._services
@property
def supperted_features(self):
"""Return a list of supported host features."""
@@ -41,18 +62,32 @@ class HostManager(CoreSysAttributes):
features.extend([
FEATURES_REBOOT,
FEATURES_SHUTDOWN,
FEATURES_SERVICES,
])
if self.sys_dbus.hostname.is_connected:
features.append(FEATURES_HOSTNAME)
if self.sys_hassos.available:
features.append(FEATURES_HASSOS)
return features
async def load(self):
"""Load host functions."""
async def reload(self):
"""Reload host functions."""
if self.sys_dbus.hostname.is_connected:
await self.info.update()
def reload(self):
"""Reload host information."""
return self.load()
if self.sys_dbus.systemd.is_connected:
await self.services.update()
async def load(self):
"""Load host information."""
with suppress(HassioError):
await self.reload()
# Load profile data
try:
await self.apparmor.load()
except HassioError as err:
_LOGGER.waring("Load host AppArmor on start fails: %s", err)

View File

@@ -81,7 +81,7 @@ class AlsaAudio(CoreSysAttributes):
@staticmethod
def _audio_database():
"""Read local json audio data into dict."""
json_file = Path(__file__).parent.joinpath('audiodb.json')
json_file = Path(__file__).parent.joinpath("data/audiodb.json")
try:
# pylint: disable=no-member
@@ -116,12 +116,12 @@ class AlsaAudio(CoreSysAttributes):
return self._default
def asound(self, alsa_input=None, alsa_output=None):
"""Generate a asound data."""
"""Generate an asound data."""
alsa_input = alsa_input or self.default.input
alsa_output = alsa_output or self.default.output
# Read Template
asound_file = Path(__file__).parent.joinpath('asound.tmpl')
asound_file = Path(__file__).parent.joinpath("data/asound.tmpl")
try:
# pylint: disable=no-member
with asound_file.open('r') as asound:

121
hassio/host/apparmor.py Normal file
View File

@@ -0,0 +1,121 @@
"""AppArmor control for host."""
import logging
import shutil
from pathlib import Path
from ..coresys import CoreSysAttributes
from ..exceptions import DBusError, HostAppArmorError
from ..utils.apparmor import validate_profile
_LOGGER = logging.getLogger(__name__)
SYSTEMD_SERVICES = {'hassos-apparmor.service', 'hassio-apparmor.service'}
class AppArmorControl(CoreSysAttributes):
"""Handle host apparmor controls."""
def __init__(self, coresys):
"""Initialize host power handling."""
self.coresys = coresys
self._profiles = set()
self._service = None
@property
def available(self):
"""Return True if AppArmor is availabe on host."""
return self._service is not None
def exists(self, profile):
"""Return True if a profile exists."""
return profile in self._profiles
async def _reload_service(self):
"""Reload internal service."""
try:
await self.sys_host.services.reload(self._service)
except DBusError as err:
_LOGGER.error("Can't reload %s: %s", self._service, err)
def _get_profile(self, profile_name):
"""Get a profile from AppArmor store."""
if profile_name not in self._profiles:
_LOGGER.error("Can't find %s for removing", profile_name)
raise HostAppArmorError()
return Path(self.sys_config.path_apparmor, profile_name)
async def load(self):
"""Load available profiles."""
for content in self.sys_config.path_apparmor.iterdir():
if not content.is_file():
continue
self._profiles.add(content.name)
# Is connected with systemd?
_LOGGER.info("Load AppArmor Profiles: %s", self._profiles)
for service in SYSTEMD_SERVICES:
if not self.sys_host.services.exists(service):
continue
self._service = service
# Load profiles
if self.available:
await self._reload_service()
else:
_LOGGER.info("AppArmor is not enabled on Host")
async def load_profile(self, profile_name, profile_file):
"""Load/Update a new/exists profile into AppArmor."""
if not validate_profile(profile_name, profile_file):
_LOGGER.error("profile is not valid with name %s", profile_name)
raise HostAppArmorError()
# Copy to AppArmor folder
dest_profile = Path(self.sys_config.path_apparmor, profile_name)
try:
shutil.copy(profile_file, dest_profile)
except OSError as err:
_LOGGER.error("Can't copy %s: %s", profile_file, err)
raise HostAppArmorError() from None
# Load profiles
_LOGGER.info("Add or Update AppArmor profile: %s", profile_name)
self._profiles.add(profile_name)
if self.available:
await self._reload_service()
async def remove_profile(self, profile_name):
"""Remove a AppArmor profile."""
profile_file = self._get_profile(profile_name)
# Only remove file
if not self.available:
try:
profile_file.unlink()
except OSError as err:
_LOGGER.error("Can't remove profile: %s", err)
raise HostAppArmorError()
return
# Marks als remove and start host process
remove_profile = Path(
self.sys_config.path_apparmor, 'remove', profile_name)
try:
profile_file.rename(remove_profile)
except OSError as err:
_LOGGER.error("Can't mark profile as remove: %s", err)
raise HostAppArmorError()
_LOGGER.info("Remove AppArmor profile: %s", profile_name)
self._profiles.remove(profile_name)
await self._reload_service()
def backup_profile(self, profile_name, backup_file):
"""Backup A profile into a new file."""
profile_file = self._get_profile(profile_name)
try:
shutil.copy(profile_file, backup_file)
except OSError as err:
_LOGGER.error("Can't backup profile %s: %s", profile_name, err)
raise HostAppArmorError()

View File

@@ -6,6 +6,9 @@ from ..exceptions import HostNotSupportedError
_LOGGER = logging.getLogger(__name__)
MANAGER = 'manager'
HOSTNAME = 'hostname'
class SystemControl(CoreSysAttributes):
"""Handle host power controls."""
@@ -14,15 +17,19 @@ class SystemControl(CoreSysAttributes):
"""Initialize host power handling."""
self.coresys = coresys
def _check_systemd(self):
def _check_dbus(self, flag):
"""Check if systemd is connect or raise error."""
if not self.sys_dbus.systemd.is_connected:
_LOGGER.error("No systemd dbus connection available")
raise HostNotSupportedError()
if flag == MANAGER and self.sys_dbus.systemd.is_connected:
return
if flag == HOSTNAME and self.sys_dbus.hostname.is_connected:
return
_LOGGER.error("No %s dbus connection available", flag)
raise HostNotSupportedError()
async def reboot(self):
"""Reboot host system."""
self._check_systemd()
self._check_dbus(MANAGER)
_LOGGER.info("Initialize host reboot over systemd")
try:
@@ -32,7 +39,7 @@ class SystemControl(CoreSysAttributes):
async def shutdown(self):
"""Shutdown host system."""
self._check_systemd()
self._check_dbus(MANAGER)
_LOGGER.info("Initialize host power off over systemd")
try:
@@ -42,9 +49,7 @@ class SystemControl(CoreSysAttributes):
async def set_hostname(self, hostname):
"""Set local a new Hostname."""
if not self.sys_dbus.systemd.is_connected:
_LOGGER.error("No hostname dbus connection available")
raise HostNotSupportedError()
self._check_dbus(HOSTNAME)
_LOGGER.info("Set Hostname %s", hostname)
await self.sys_dbus.hostname.set_static_hostname(hostname)

View File

@@ -1,4 +1,4 @@
"""Power control for host."""
"""Info control for host."""
import logging
from ..coresys import CoreSysAttributes
@@ -47,7 +47,7 @@ class InfoCenter(CoreSysAttributes):
async def update(self):
"""Update properties over dbus."""
if not self.sys_dbus.systemd.is_connected:
if not self.sys_dbus.hostname.is_connected:
_LOGGER.error("No hostname dbus connection available")
raise HostNotSupportedError()

99
hassio/host/services.py Normal file
View File

@@ -0,0 +1,99 @@
"""Service control for host."""
import logging
import attr
from ..coresys import CoreSysAttributes
from ..exceptions import HassioError, HostNotSupportedError, HostServiceError
_LOGGER = logging.getLogger(__name__)
MOD_REPLACE = 'replace'
class ServiceManager(CoreSysAttributes):
"""Handle local service information controls."""
def __init__(self, coresys):
"""Initialize system center handling."""
self.coresys = coresys
self._services = set()
def __iter__(self):
"""Iterator trought services."""
return iter(self._services)
def _check_dbus(self, unit=None):
"""Check available dbus connection."""
if not self.sys_dbus.systemd.is_connected:
_LOGGER.error("No systemd dbus connection available")
raise HostNotSupportedError()
if unit and not self.exists(unit):
_LOGGER.error("Unit '%s' not found", unit)
raise HostServiceError()
def start(self, unit):
"""Start a service on host."""
self._check_dbus(unit)
_LOGGER.info("Start local service %s", unit)
return self.sys_dbus.systemd.start_unit(unit, MOD_REPLACE)
def stop(self, unit):
"""Stop a service on host."""
self._check_dbus(unit)
_LOGGER.info("Stop local service %s", unit)
return self.sys_dbus.systemd.stop_unit(unit, MOD_REPLACE)
def reload(self, unit):
"""Reload a service on host."""
self._check_dbus(unit)
_LOGGER.info("Reload local service %s", unit)
return self.sys_dbus.systemd.reload_unit(unit, MOD_REPLACE)
def restart(self, unit):
"""Restart a service on host."""
self._check_dbus(unit)
_LOGGER.info("Restart local service %s", unit)
return self.sys_dbus.systemd.restart_unit(unit, MOD_REPLACE)
def exists(self, unit):
"""Check if a unit exists and return True."""
for service in self._services:
if unit == service.name:
return True
return False
async def update(self):
"""Update properties over dbus."""
self._check_dbus()
_LOGGER.info("Update service information")
self._services.clear()
try:
systemd_units = await self.sys_dbus.systemd.list_units()
for service_data in systemd_units[0]:
if not service_data[0].endswith(".service") or \
service_data[2] != 'loaded':
continue
self._services.add(ServiceInfo.read_from(service_data))
except (HassioError, IndexError):
_LOGGER.warning("Can't update host service information!")
@attr.s(frozen=True)
class ServiceInfo:
"""Represent a single Service."""
name = attr.ib(type=str)
description = attr.ib(type=str)
state = attr.ib(type=str)
@staticmethod
def read_from(unit):
"""Parse data from dbus into this object."""
return ServiceInfo(unit[0], unit[1], unit[3])

View File

@@ -24,7 +24,7 @@ RE_TTY = re.compile(r"tty[A-Z]+")
class Hardware:
"""Represent a interface to procfs, sysfs and udev."""
"""Represent an interface to procfs, sysfs and udev."""
def __init__(self):
"""Init hardware object."""
@@ -63,6 +63,10 @@ class Hardware:
@property
def audio_devices(self):
"""Return all available audio interfaces."""
if not ASOUND_CARDS.exists():
_LOGGER.info("No audio devices found")
return {}
try:
with ASOUND_CARDS.open('r') as cards_file:
cards = cards_file.read()

View File

@@ -56,7 +56,7 @@ class Discovery(CoreSysAttributes):
"""Send a discovery message to Home-Assistant."""
message = Message(provider, component, platform, config)
# Allready exists?
# Already exists?
for exists_message in self.message_obj:
if exists_message == message:
_LOGGER.warning("Found douplicate discovery message from %s",

View File

@@ -92,9 +92,9 @@ class SnapshotManager(CoreSysAttributes):
if not await snapshot.load():
return None
# Allready exists?
# Already exists?
if snapshot.slug in self.snapshots_obj:
_LOGGER.error("Snapshot %s allready exists!", snapshot.slug)
_LOGGER.error("Snapshot %s already exists!", snapshot.slug)
return None
# Move snapshot to backup

View File

@@ -137,7 +137,7 @@ class Snapshot(CoreSysAttributes):
self._data[ATTR_CRYPTO] = CRYPTO_AES128
def set_password(self, password):
"""Set the password for a exists snapshot."""
"""Set the password for an existing snapshot."""
if not password:
return False
@@ -210,7 +210,7 @@ class Snapshot(CoreSysAttributes):
if not self.tarfile.is_file():
return self
# extract a exists snapshot
# extract an existing snapshot
def _extract_snapshot():
"""Extract a snapshot."""
with tarfile.open(self.tarfile, "r:") as tar:
@@ -252,7 +252,7 @@ class Snapshot(CoreSysAttributes):
addon_list = addon_list or self.sys_addons.list_installed
async def _addon_save(addon):
"""Task to store a add-on into snapshot."""
"""Task to store an add-on into snapshot."""
addon_file = SecureTarFile(
Path(self._tmp.name, f"{addon.slug}.tar.gz"),
'w', key=self._key)
@@ -285,7 +285,7 @@ class Snapshot(CoreSysAttributes):
addon_list.append(addon)
async def _addon_restore(addon):
"""Task to restore a add-on into snapshot."""
"""Task to restore an add-on into snapshot."""
addon_file = SecureTarFile(
Path(self._tmp.name, f"{addon.slug}.tar.gz"),
'r', key=self._key)

View File

@@ -25,7 +25,7 @@ def password_for_validating(password):
def key_to_iv(key):
"""Generate a iv from Key."""
"""Generate an iv from Key."""
for _ in range(100):
key = hashlib.sha256(key).digest()
return key[:16]

View File

@@ -15,7 +15,7 @@ ALL_FOLDERS = [FOLDER_HOMEASSISTANT, FOLDER_SHARE, FOLDER_ADDONS, FOLDER_SSL]
def unique_addons(addons_list):
"""Validate that a add-on is unique."""
"""Validate that an add-on is unique."""
single = set([addon[ATTR_SLUG] for addon in addons_list])
if len(single) != len(addons_list):
@@ -39,7 +39,7 @@ SCHEMA_SNAPSHOT = vol.Schema({
vol.Optional(ATTR_BOOT, default=True): vol.Boolean(),
vol.Optional(ATTR_SSL, default=False): vol.Boolean(),
vol.Optional(ATTR_PORT, default=8123): NETWORK_PORT,
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)),
vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(),
vol.Optional(ATTR_WAIT_BOOT, default=600):
vol.All(vol.Coerce(int), vol.Range(min=60)),

View File

@@ -1,8 +1,14 @@
"""HomeAssistant control object."""
import logging
from pathlib import Path
from tempfile import TemporaryDirectory
import aiohttp
from .coresys import CoreSysAttributes
from .docker.supervisor import DockerSupervisor
from .const import URL_HASSIO_APPARMOR
from .exceptions import HostAppArmorError
_LOGGER = logging.getLogger(__name__)
@@ -23,7 +29,7 @@ class Supervisor(CoreSysAttributes):
@property
def need_update(self):
"""Return True if a update is available."""
"""Return True if an update is available."""
return self.version != self.last_version
@property
@@ -46,6 +52,31 @@ class Supervisor(CoreSysAttributes):
"""Return arch of hass.io containter."""
return self.instance.arch
async def update_apparmor(self):
"""Fetch last version and update profile."""
url = URL_HASSIO_APPARMOR
try:
_LOGGER.info("Fetch AppArmor profile %s", url)
async with self.sys_websession.get(url, timeout=10) as request:
data = await request.text()
except aiohttp.ClientError as err:
_LOGGER.warning("Can't fetch AppArmor profile: %s", err)
return
with TemporaryDirectory(dir=self.sys_config.path_tmp) as tmp_dir:
profile_file = Path(tmp_dir, 'apparmor.txt')
try:
profile_file.write_text(data)
except OSError as err:
_LOGGER.error("Can't write temporary profile: %s", err)
return
try:
await self.sys_host.apparmor.load_profile(
"hassio-supervisor", profile_file)
except HostAppArmorError:
_LOGGER.error("Can't update AppArmor profile!")
async def update(self, version=None):
"""Update HomeAssistant version."""
version = version or self.last_version
@@ -56,6 +87,7 @@ class Supervisor(CoreSysAttributes):
_LOGGER.info("Update supervisor to version %s", version)
if await self.instance.install(version):
await self.update_apparmor()
self.sys_loop.call_later(1, self.sys_loop.stop)
return True

View File

@@ -41,7 +41,7 @@ class Tasks(CoreSysAttributes):
self.jobs.add(self.sys_scheduler.register_task(
self.sys_snapshots.reload, self.RUN_RELOAD_SNAPSHOTS))
self.jobs.add(self.sys_scheduler.register_task(
self.sys_host.load, self.RUN_RELOAD_HOST))
self.sys_host.reload, self.RUN_RELOAD_HOST))
self.jobs.add(self.sys_scheduler.register_task(
self._watchdog_homeassistant_docker,
@@ -53,7 +53,7 @@ class Tasks(CoreSysAttributes):
_LOGGER.info("All core tasks are scheduled")
async def _update_addons(self):
"""Check if a update is available of a addon and update it."""
"""Check if an update is available for an addon and update it."""
tasks = []
for addon in self.sys_addons.list_addons:
if not addon.is_installed or not addon.auto_update:
@@ -77,7 +77,7 @@ class Tasks(CoreSysAttributes):
if not self.sys_supervisor.need_update:
return
# don't perform a update on beta/dev channel
# don't perform an update on beta/dev channel
if self.sys_dev:
_LOGGER.warning("Ignore Hass.io update on dev channel!")
return

View File

@@ -1,15 +1,15 @@
"""Fetch last versions from webserver."""
import asyncio
from contextlib import suppress
from datetime import timedelta
import json
import logging
import aiohttp
import async_timeout
from .const import (
URL_HASSIO_VERSION, FILE_HASSIO_UPDATER, ATTR_HOMEASSISTANT, ATTR_HASSIO,
ATTR_CHANNEL, CHANNEL_STABLE, CHANNEL_BETA, CHANNEL_DEV)
ATTR_CHANNEL)
from .coresys import CoreSysAttributes
from .utils import AsyncThrottle
from .utils.json import JsonConfig
@@ -17,12 +17,6 @@ from .validate import SCHEMA_UPDATER_CONFIG
_LOGGER = logging.getLogger(__name__)
CHANNEL_TO_BRANCH = {
CHANNEL_STABLE: 'master',
CHANNEL_BETA: 'rc',
CHANNEL_DEV: 'dev',
}
class Updater(JsonConfig, CoreSysAttributes):
"""Fetch last versions from version.json."""
@@ -65,12 +59,11 @@ class Updater(JsonConfig, CoreSysAttributes):
Is a coroutine.
"""
url = URL_HASSIO_VERSION.format(CHANNEL_TO_BRANCH[self.channel])
url = URL_HASSIO_VERSION.format(channel=self.channel)
try:
_LOGGER.info("Fetch update data from %s", url)
with async_timeout.timeout(10):
async with self.sys_websession.get(url) as request:
data = await request.json(content_type=None)
async with self.sys_websession.get(url, timeout=10) as request:
data = await request.json(content_type=None)
except (aiohttp.ClientError, asyncio.TimeoutError, KeyError) as err:
_LOGGER.warning("Can't fetch versions from %s: %s", url, err)
@@ -81,11 +74,18 @@ class Updater(JsonConfig, CoreSysAttributes):
return
# data valid?
if not data:
if not data or data.get(ATTR_CHANNEL) != self.channel:
_LOGGER.warning("Invalid data from %s", url)
return
# update versions
self._data[ATTR_HOMEASSISTANT] = data.get('homeassistant')
self._data[ATTR_HASSIO] = data.get('hassio')
# update supervisor versions
with suppress(KeyError):
self._data[ATTR_HASSIO] = data['supervisor']
# update Home Assistant version
machine = self.sys_machine or 'default'
with suppress(KeyError):
self._data[ATTR_HOMEASSISTANT] = \
data['homeassistant'][machine]
self.save_data()

66
hassio/utils/apparmor.py Normal file
View File

@@ -0,0 +1,66 @@
"""Some functions around apparmor profiles."""
import logging
import re
from ..exceptions import AppArmorFileError, AppArmorInvalidError
_LOGGER = logging.getLogger(__name__)
RE_PROFILE = re.compile(r"^profile ([^ ]+).*$")
def get_profile_name(profile_file):
"""Read the profile name from file."""
profiles = set()
try:
with profile_file.open('r') as profile_data:
for line in profile_data:
match = RE_PROFILE.match(line)
if not match:
continue
profiles.add(match.group(1))
except OSError as err:
_LOGGER.error("Can't read apparmor profile: %s", err)
raise AppArmorFileError()
if len(profiles) != 1:
_LOGGER.error("To many profiles inside file: %s", profiles)
raise AppArmorInvalidError()
return profiles.pop()
def validate_profile(profile_name, profile_file):
"""Check if profile from file is valid with profile name."""
if profile_name == get_profile_name(profile_file):
return True
return False
def adjust_profile(profile_name, profile_file, profile_new):
"""Fix the profile name."""
org_profile = get_profile_name(profile_file)
profile_data = []
# Process old data
try:
with profile_file.open('r') as profile:
for line in profile:
match = RE_PROFILE.match(line)
if not match:
profile_data.append(line)
else:
profile_data.append(
line.replace(org_profile, profile_name))
except OSError as err:
_LOGGER.error("Can't adjust origin profile: %s", err)
raise AppArmorFileError()
# Write into new file
try:
with profile_new.open('w') as profile:
profile.writelines(profile_data)
except OSError as err:
_LOGGER.error("Can't write new profile: %s", err)
raise AppArmorFileError()

View File

@@ -14,10 +14,11 @@ _LOGGER = logging.getLogger(__name__)
RE_GVARIANT_TYPE = re.compile(
r"(?:boolean|byte|int16|uint16|int32|uint32|handle|int64|uint64|double|"
r"string|objectpath|signature) ")
RE_GVARIANT_TULPE = re.compile(r"^\((.*),\)$")
RE_GVARIANT_VARIANT = re.compile(
r"(?<=(?: |{|\[))<((?:'|\").*?(?:'|\")|\d+(?:\.\d+)?)>(?=(?:|]|}|,))")
RE_GVARIANT_STRING = re.compile(r"(?<=(?: |{|\[))'(.*?)'(?=(?:|]|}|,))")
RE_GVARIANT_STRING = re.compile(r"(?<=(?: |{|\[|\())'(.*?)'(?=(?:|]|}|,|\)))")
RE_GVARIANT_TUPLE_O = re.compile(r"\"[^\"]*?\"|(\()")
RE_GVARIANT_TUPLE_C = re.compile(r"\"[^\"]*?\"|(,?\))")
# Commands for dbus
INTROSPECT = ("gdbus introspect --system --dest {bus} "
@@ -76,13 +77,16 @@ class DBus:
def _gvariant(raw):
"""Parse GVariant input to python."""
raw = RE_GVARIANT_TYPE.sub("", raw)
raw = RE_GVARIANT_TULPE.sub(r"[\1]", raw)
raw = RE_GVARIANT_VARIANT.sub(r"\1", raw)
raw = RE_GVARIANT_STRING.sub(r'"\1"', raw)
raw = RE_GVARIANT_TUPLE_O.sub(
lambda x: x.group(0) if not x.group(1) else"[", raw)
raw = RE_GVARIANT_TUPLE_C.sub(
lambda x: x.group(0) if not x.group(1) else"]", raw)
# No data
if raw.startswith("()"):
return {}
if raw.startswith("[]"):
return []
try:
return json.loads(raw)

View File

@@ -81,7 +81,7 @@ class SecureTarFile:
def _generate_iv(key, salt):
"""Generate a iv from data."""
"""Generate an iv from data."""
temp_iv = key + salt
for _ in range(100):
temp_iv = hashlib.sha256(temp_iv).digest()

View File

@@ -17,7 +17,7 @@ RE_REPOSITORY = re.compile(r"^(?P<url>[^#]+)(?:#(?P<branch>[\w\-]+))?$")
NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
WAIT_BOOT = vol.All(vol.Coerce(int), vol.Range(min=1, max=60))
DOCKER_IMAGE = vol.Match(r"^[\w{}]+/[\-\w{}]+$")
ALSA_DEVICE = vol.Any(None, vol.Match(r"\d+,\d+"))
ALSA_DEVICE = vol.Maybe(vol.Match(r"\d+,\d+"))
CHANNELS = vol.In([CHANNEL_STABLE, CHANNEL_BETA, CHANNEL_DEV])
@@ -87,7 +87,7 @@ SCHEMA_HASS_CONFIG = vol.Schema({
vol.Inclusive(ATTR_IMAGE, 'custom_hass'): DOCKER_IMAGE,
vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Coerce(str),
vol.Optional(ATTR_PORT, default=8123): NETWORK_PORT,
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)),
vol.Optional(ATTR_PASSWORD): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_SSL, default=False): vol.Boolean(),
vol.Optional(ATTR_WATCHDOG, default=True): vol.Boolean(),
vol.Optional(ATTR_WAIT_BOOT, default=600):

View File

@@ -40,14 +40,16 @@ setup(
],
include_package_data=True,
install_requires=[
'async_timeout==2.0.1',
'aiohttp==3.1.2',
'docker==3.2.0',
'attr==0.3.1',
'async_timeout==3.0.0',
'aiohttp==3.3.2',
'docker==3.3.0',
'colorlog==3.1.2',
'voluptuous==0.11.1',
'gitpython==2.1.8',
'pytz==2018.3',
'gitpython==2.1.10',
'pytz==2018.4',
'pyudev==0.21.0',
'pycryptodome==3.4.11'
'pycryptodome==3.4.11',
"cpe==1.2.1"
]
)

View File

@@ -1,8 +1,4 @@
{
"hassio": "103.3",
"homeassistant": "0.68.1",
"resinos": "1.3",
"resinhup": "0.3",
"generic": "0.3",
"cluster": "0.1"
"hassio": "108",
"homeassistant": "0.70.0"
}