Compare commits

...

31 Commits
201 ... 204

Author SHA1 Message Date
Pascal Vizeli
14167f6e13 Merge pull request #1544 from home-assistant/dev
Release 204
2020-02-29 00:30:16 +01:00
Pascal Vizeli
7a1aba6f81 Fix old alsa format settings (#1543) 2020-02-29 00:25:11 +01:00
Pascal Vizeli
920f7f2ece Support for own init on image (#1542)
* Support for own init on image

* fix params
2020-02-28 23:15:46 +01:00
Pascal Vizeli
e1cbfdd84b Support mute + applications from pulse (#1541)
* Support mute + applications from pulse

* Fix lint

* Fix application parser

* Fix type

* Add application endpoints

* error handling

* Fix
2020-02-28 17:52:12 +01:00
Pascal Vizeli
87170a4497 Restart add-ons attach to audio with update pulse (#1540) 2020-02-28 14:05:31 +01:00
Pascal Vizeli
ae6f8bd345 Bump version to 203 2020-02-28 10:57:05 +01:00
Pascal Vizeli
b9496e0972 Merge pull request #1539 from home-assistant/dev
Release 203
2020-02-28 10:56:22 +01:00
Pascal Vizeli
c36a6dcd65 Add default asound for pulse (#1538)
* Add default asound for pulse

* fix lint

* fix config
2020-02-28 01:14:43 +01:00
Pascal Vizeli
19ca836b78 Prevent using pulseaudio on event loop (#1536)
* Prevent using pulseaudio on event loop

* Fix name overwrite

* Fix value
2020-02-27 22:01:20 +01:00
Pascal Vizeli
8a6ea7ab50 Use shorter function for soundcard (#1535) 2020-02-27 17:22:12 +01:00
Pascal Vizeli
6721b8f265 Expose sound cards and profiles with endpoint (#1534)
* Expose sound cards and profiles with endpoint

* Fix naming

* Fix issue

* Update API
2020-02-27 16:25:04 +01:00
Pascal Vizeli
9393521f98 Update Panel for audio (#1533) 2020-02-27 13:47:46 +01:00
Pascal Vizeli
398b24e0ab Fix homeassistant config check with overlay-s6 (#1532) 2020-02-27 13:29:42 +01:00
Pascal Vizeli
374bcf8073 Adjust sound reload (#1531)
* Adjust sound reload & remove quirk

* clean info message

* fix hack
2020-02-27 11:58:28 +01:00
Pascal Vizeli
7e3859e2f5 Observe host hardware for realtime actions (#1530)
* Observe host hardware for realtime actions

* Better logging

* fix testenv
2020-02-27 10:31:35 +01:00
Pascal Vizeli
490ec0d462 Bump version to 203 2020-02-26 14:47:38 +01:00
Pascal Vizeli
15bf1ee50e Merge pull request #1528 from home-assistant/dev
Release 202
2020-02-26 14:46:50 +01:00
Pascal Vizeli
6376d92a0d Merge remote-tracking branch 'origin/master' into dev 2020-02-26 13:38:12 +00:00
Pascal Vizeli
10230b0b4c Support profiles on template (#1527) 2020-02-26 14:28:09 +01:00
Pascal Vizeli
2495cda5ec Add Pulse audio control basics (#1525)
* Add Pulse audio control basics

* add functionality

* Fix handling

* Give access to all

* Fix latest issues

* revert docker

* Fix pipeline
2020-02-26 11:48:11 +01:00
Pascal Vizeli
ae8ddca040 Delete entry.sh 2020-02-25 18:38:52 +01:00
Pascal Vizeli
0212d027fb Add Audio layer / PulseAudio (#1523)
* Improve alsa handling

* use default from image

* create alsa folder

* Map config into addon

* Add Audio object

* Fix dbus

* add host group file

* Fix persistent file

* Use new template

* fix lint

* Fix lint

* add API

* Update new base image / build system

* Add audio container

* extend new audio settings

* provide pulse client config

* Adjust files

* Use without auth

* reset did not exists now

* cleanup old alsa layer

* fix tasks

* fix black

* fix lint

* Add dbus support

* add dbus adjustments

* Fixups
2020-02-25 18:37:06 +01:00
dependabot-preview[bot]
a3096153ab Bump gitpython from 3.0.8 to 3.1.0 (#1524)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.0.8 to 3.1.0.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/master/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.0.8...3.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-25 18:30:11 +01:00
dependabot-preview[bot]
7434ca9e99 Bump gitpython from 3.0.8 to 3.1.0 (#1524)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.0.8 to 3.1.0.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/master/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.0.8...3.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-25 18:29:44 +01:00
Pascal Vizeli
4ac7f7dcf0 Rename Hass.io -> Supervisor (#1522)
* Rename Hass.io -> Supervisor

* part 2

* fix lint

* fix auth name
2020-02-21 17:55:41 +01:00
Pascal Vizeli
e9f5b13aa5 Fix wrong last boot (#1521)
* Protect overwrite last boot uptime

* Fix naming

* Fix lint
2020-02-20 21:37:59 +01:00
Pascal Vizeli
1fbb6d46ea Fix webui option (#1519) 2020-02-20 09:17:53 +01:00
Pascal Vizeli
8dbfea75b1 Extend video & add tests (#1518) 2020-02-20 00:29:48 +01:00
zewelor
3b3840c087 Update hardware.py (#1516)
Allow to pass video* devices to containers. Should allow to use usb webcams / uvc tuners.
2020-02-19 09:08:11 +01:00
dependabot-preview[bot]
a21353909d Bump gitpython from 3.0.7 to 3.0.8 (#1513)
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.0.7 to 3.0.8.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/master/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.0.7...3.0.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-17 16:58:00 +01:00
Pascal Vizeli
5497ed885a Bump version to 202 2020-02-17 11:38:08 +01:00
278 changed files with 2267 additions and 649 deletions

View File

@@ -33,6 +33,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
containerd.io \
&& rm -rf /var/lib/apt/lists/*
# Install tools
RUN apt-get update && apt-get install -y --no-install-recommends \
jq \
dbus \
network-manager \
libpulse0 \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies from requirements.txt if it exists
COPY requirements.txt requirements_tests.txt ./
RUN pip3 install -r requirements.txt -r requirements_tests.txt \

View File

@@ -1,31 +1,24 @@
// See https://aka.ms/vscode-remote/devcontainer.json for format details.
{
"name": "Hass.io dev",
"context": "..",
"dockerFile": "Dockerfile",
"appPort": "9123:8123",
"runArgs": [
"-e",
"GIT_EDITOR=code --wait",
"--privileged"
],
"extensions": [
"ms-python.python",
"visualstudioexptteam.vscodeintellicode",
"esbenp.prettier-vscode"
],
"settings": {
"python.pythonPath": "/usr/local/bin/python",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black",
"python.formatting.blackArgs": [
"--target-version",
"py37"
],
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true
}
}
"name": "Supervisor dev",
"context": "..",
"dockerFile": "Dockerfile",
"appPort": "9123:8123",
"runArgs": ["-e", "GIT_EDITOR=code --wait", "--privileged"],
"extensions": [
"ms-python.python",
"visualstudioexptteam.vscodeintellicode",
"esbenp.prettier-vscode"
],
"settings": {
"python.pythonPath": "/usr/local/bin/python",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black",
"python.formatting.blackArgs": ["--target-version", "py37"],
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true
}
}

View File

@@ -14,10 +14,10 @@
# virtualenv
venv/
# HA
home-assistant-polymer/*
misc/*
script/*
# Data
home-assistant-polymer/
script/
tests/
# Test ENV
data/

191
API.md
View File

@@ -853,6 +853,197 @@ return:
}
```
### Audio
- GET `/audio/info`
```json
{
"host": "ip-address",
"version": "1",
"latest_version": "2",
"audio": {
"card": [
{
"name": "...",
"index": 1,
"driver": "...",
"profiles": [
{
"name": "...",
"description": "...",
"active": false
}
]
}
],
"input": [
{
"name": "...",
"index": 0,
"description": "...",
"volume": 0.3,
"mute": false,
"default": false,
"card": "null|int",
"applications": [
{
"name": "...",
"index": 0,
"stream_index": 0,
"stream_type": "INPUT",
"volume": 0.3,
"mute": false,
"addon": ""
}
]
}
],
"output": [
{
"name": "...",
"index": 0,
"description": "...",
"volume": 0.3,
"mute": false,
"default": false,
"card": "null|int",
"applications": [
{
"name": "...",
"index": 0,
"stream_index": 0,
"stream_type": "OUTPUT",
"volume": 0.3,
"mute": false,
"addon": ""
}
]
}
],
"application": [
{
"name": "...",
"index": 0,
"stream_index": 0,
"stream_type": "OUTPUT",
"volume": 0.3,
"mute": false,
"addon": ""
}
]
}
}
```
- POST `/audio/update`
```json
{
"version": "VERSION"
}
```
- POST `/audio/restart`
- POST `/audio/reload`
- GET `/audio/logs`
- POST `/audio/volume/input`
```json
{
"index": "...",
"volume": 0.5
}
```
- POST `/audio/volume/output`
```json
{
"index": "...",
"volume": 0.5
}
```
- POST `/audio/volume/{output|input}/application`
```json
{
"index": "...",
"volume": 0.5
}
```
- POST `/audio/mute/input`
```json
{
"index": "...",
"active": false
}
```
- POST `/audio/mute/output`
```json
{
"index": "...",
"active": false
}
```
- POST `/audio/mute/{output|input}/application`
```json
{
"index": "...",
"active": false
}
```
- POST `/audio/default/input`
```json
{
"name": "..."
}
```
- POST `/audio/default/output`
```json
{
"name": "..."
}
```
- POST `/audio/profile`
```json
{
"card": "...",
"name": "..."
}
```
- GET `/audio/stats`
```json
{
"cpu_percent": 0.0,
"memory_usage": 283123,
"memory_limit": 329392,
"memory_percent": 1.4,
"network_tx": 0,
"network_rx": 0,
"blk_read": 0,
"blk_write": 0
}
```
### Auth / SSO API
You can use the user system on homeassistant. We handle this auth system on

View File

@@ -3,14 +3,15 @@ FROM $BUILD_FROM
# Install base
RUN apk add --no-cache \
openssl \
libffi \
musl \
git \
socat \
glib \
eudev \
eudev-libs
eudev-libs \
git \
glib \
libffi \
libpulse \
musl \
openssl \
socat
ARG BUILD_ARCH
WORKDIR /usr/src
@@ -23,15 +24,11 @@ RUN export MAKEFLAGS="-j$(nproc)" \
-r ./requirements.txt \
&& rm -f requirements.txt
# Install HassIO
COPY . hassio
RUN pip3 install --no-cache-dir -e ./hassio \
&& python3 -m compileall ./hassio/hassio
# Install Home Assistant Supervisor
COPY . supervisor
RUN pip3 install --no-cache-dir -e ./supervisor \
&& python3 -m compileall ./supervisor/supervisor
# Initialize udev daemon, handle CMD
COPY entry.sh /bin/
ENTRYPOINT ["/bin/entry.sh"]
WORKDIR /
CMD [ "python3", "-m", "hassio" ]
COPY rootfs /

View File

@@ -1,3 +1,3 @@
include LICENSE.md
graft hassio
graft supervisor
recursive-exclude * *.py[co]

View File

@@ -1,6 +1,6 @@
[![Build Status](https://dev.azure.com/home-assistant/Hass.io/_apis/build/status/hassio?branchName=dev)](https://dev.azure.com/home-assistant/Hass.io/_build/latest?definitionId=2&branchName=dev)
# Hass.io
# Home Assistant Supervisor
## First private cloud solution for home automation
@@ -10,8 +10,6 @@ communicates with the Supervisor. The Supervisor provides an API to manage the
installation. This includes changing network settings or installing
and updating software.
![](misc/hassio.png?raw=true)
## Installation
Installation instructions can be found at <https://home-assistant.io/hassio>.

View File

@@ -17,6 +17,10 @@ jobs:
pool:
vmImage: "ubuntu-latest"
steps:
- script: |
sudo apt-get update
sudo apt-get install -y libpulse0 libudev1
displayName: "Install Host library"
- task: UsePythonVersion@0
displayName: "Use Python 3.7"
inputs:

View File

@@ -10,10 +10,8 @@ trigger:
- "*"
pr: none
variables:
- name: basePythonTag
value: "3.7-alpine3.11"
- name: versionBuilder
value: "6.9"
value: "7.0"
- group: docker
jobs:
@@ -51,6 +49,5 @@ jobs:
-v ~/.docker:/root/.docker \
-v /run/docker.sock:/run/docker.sock:rw -v $(pwd):/data:ro \
homeassistant/amd64-builder:$(versionBuilder) \
--supervisor $(basePythonTag) --version $(Build.SourceBranchName) \
--all -t /data --docker-hub homeassistant
--generic $(Build.SourceBranchName) --all -t /data
displayName: "Build Release"

13
build.json Normal file
View File

@@ -0,0 +1,13 @@
{
"image": "homeassistant/{arch}-hassio-supervisor",
"build_from": {
"aarch64": "homeassistant/aarch64-base-python:3.7-alpine3.11",
"armhf": "homeassistant/armhf-base-python:3.7-alpine3.11",
"armv7": "homeassistant/armv7-base-python:3.7-alpine3.11",
"amd64": "homeassistant/amd64-base-python:3.7-alpine3.11",
"i386": "homeassistant/i386-base-python:3.7-alpine3.11"
},
"labels": {
"io.hass.type": "supervisor"
}
}

View File

@@ -1,13 +0,0 @@
#!/bin/bash
set -e
udevd --daemon
udevadm trigger
if CMD="$(command -v "$1")"; then
shift
exec "$CMD" "$@"
else
echo "Command not found: $1"
exit 1
fi

View File

@@ -1 +0,0 @@
"""Init file for Hass.io."""

Binary file not shown.

View File

@@ -1,43 +0,0 @@
{
"vendors~confirmation~hassio-addon-view.js": "/api/hassio/app/chunk.87b1d37fc9b8a6f7e2a6.js",
"vendors~confirmation~hassio-addon-view.js.map": "/api/hassio/app/chunk.87b1d37fc9b8a6f7e2a6.js.map",
"vendors~hassio-icons~hassio-main.js": "/api/hassio/app/chunk.e46c606dd9100816af4e.js",
"vendors~hassio-icons~hassio-main.js.map": "/api/hassio/app/chunk.e46c606dd9100816af4e.js.map",
"codemirror.js": "/api/hassio/app/chunk.92a11ac1b80e0d7839d2.js",
"codemirror.js.map": "/api/hassio/app/chunk.92a11ac1b80e0d7839d2.js.map",
"confirmation.js": "/api/hassio/app/chunk.429840c83fad61bc51a8.js",
"confirmation.js.map": "/api/hassio/app/chunk.429840c83fad61bc51a8.js.map",
"dialog-hassio-markdown.js": "/api/hassio/app/chunk.715824f4764bdbe425b1.js",
"dialog-hassio-markdown.js.map": "/api/hassio/app/chunk.715824f4764bdbe425b1.js.map",
"dialog-hassio-snapshot.js": "/api/hassio/app/chunk.9d371c8143226d4eaaee.js",
"dialog-hassio-snapshot.js.map": "/api/hassio/app/chunk.9d371c8143226d4eaaee.js.map",
"entrypoint.js": "/api/hassio/app/entrypoint.js",
"entrypoint.js.map": "/api/hassio/app/entrypoint.js.map",
"hassio-addon-view.js": "/api/hassio/app/chunk.43e40fd69686ad51301d.js",
"hassio-addon-view.js.map": "/api/hassio/app/chunk.43e40fd69686ad51301d.js.map",
"hassio-icons.js": "/api/hassio/app/chunk.0b82745c7bdffe5c1404.js",
"hassio-icons.js.map": "/api/hassio/app/chunk.0b82745c7bdffe5c1404.js.map",
"hassio-ingress-view.js": "/api/hassio/app/chunk.990ee58006b248f55d23.js",
"hassio-ingress-view.js.map": "/api/hassio/app/chunk.990ee58006b248f55d23.js.map",
"hassio-main.js": "/api/hassio/app/chunk.4d45ee0a3d852768f97e.js",
"hassio-main.js.map": "/api/hassio/app/chunk.4d45ee0a3d852768f97e.js.map",
"mdi-icons.js": "/api/hassio/app/chunk.b60200a57d6f63941b30.js",
"mdi-icons.js.map": "/api/hassio/app/chunk.b60200a57d6f63941b30.js.map",
"roboto.js": "/api/hassio/app/chunk.b2dce600432c76a53d8c.js",
"roboto.js.map": "/api/hassio/app/chunk.b2dce600432c76a53d8c.js.map",
"vendors~codemirror.js": "/api/hassio/app/chunk.8527374a266cecf93aa9.js",
"vendors~codemirror.js.map": "/api/hassio/app/chunk.8527374a266cecf93aa9.js.map",
"vendors~hassio-addon-view.js": "/api/hassio/app/chunk.f49e500cf58ea310d452.js",
"vendors~hassio-addon-view.js.map": "/api/hassio/app/chunk.f49e500cf58ea310d452.js.map",
"vendors~hassio-main.js": "/api/hassio/app/chunk.d4931d72592ad48ba2be.js",
"vendors~hassio-main.js.map": "/api/hassio/app/chunk.d4931d72592ad48ba2be.js.map",
"201359fd5a526afe13ef.worker.js": "/api/hassio/app/201359fd5a526afe13ef.worker.js",
"201359fd5a526afe13ef.worker.js.map": "/api/hassio/app/201359fd5a526afe13ef.worker.js.map",
"chunk.429840c83fad61bc51a8.js.LICENSE": "/api/hassio/app/chunk.429840c83fad61bc51a8.js.LICENSE",
"chunk.715824f4764bdbe425b1.js.LICENSE": "/api/hassio/app/chunk.715824f4764bdbe425b1.js.LICENSE",
"chunk.87b1d37fc9b8a6f7e2a6.js.LICENSE": "/api/hassio/app/chunk.87b1d37fc9b8a6f7e2a6.js.LICENSE",
"chunk.9d371c8143226d4eaaee.js.LICENSE": "/api/hassio/app/chunk.9d371c8143226d4eaaee.js.LICENSE",
"chunk.d4931d72592ad48ba2be.js.LICENSE": "/api/hassio/app/chunk.d4931d72592ad48ba2be.js.LICENSE",
"chunk.e46c606dd9100816af4e.js.LICENSE": "/api/hassio/app/chunk.e46c606dd9100816af4e.js.LICENSE",
"chunk.f49e500cf58ea310d452.js.LICENSE": "/api/hassio/app/chunk.f49e500cf58ea310d452.js.LICENSE"
}

View File

@@ -1,17 +0,0 @@
pcm.!default {
type asym
capture.pcm "mic"
playback.pcm "speaker"
}
pcm.mic {
type plug
slave {
pcm "hw:$input"
}
}
pcm.speaker {
type plug
slave {
pcm "hw:$output"
}
}

View File

@@ -1,18 +0,0 @@
{
"raspberrypi3": {
"bcm2835 - bcm2835 ALSA": {
"0,0": "Raspberry Jack",
"0,1": "Raspberry HDMI"
},
"output": "0,0",
"input": null
},
"raspberrypi2": {
"output": "0,0",
"input": null
},
"raspberrypi": {
"output": "0,0",
"input": null
}
}

View File

@@ -1,138 +0,0 @@
"""Host Audio support."""
import logging
import json
from pathlib import Path
from string import Template
import attr
from ..const import ATTR_INPUT, ATTR_OUTPUT, ATTR_DEVICES, ATTR_NAME, CHAN_ID, CHAN_TYPE
from ..coresys import CoreSysAttributes
_LOGGER: logging.Logger = logging.getLogger(__name__)
@attr.s()
class DefaultConfig:
"""Default config input/output ALSA channel."""
input: str = attr.ib()
output: str = attr.ib()
AUDIODB_JSON: Path = Path(__file__).parents[1].joinpath("data/audiodb.json")
ASOUND_TMPL: Path = Path(__file__).parents[1].joinpath("data/asound.tmpl")
class AlsaAudio(CoreSysAttributes):
"""Handle Audio ALSA host data."""
def __init__(self, coresys):
"""Initialize ALSA audio system."""
self.coresys = coresys
self._data = {ATTR_INPUT: {}, ATTR_OUTPUT: {}}
self._cache = 0
self._default = None
@property
def input_devices(self):
"""Return list of ALSA input devices."""
self._update_device()
return self._data[ATTR_INPUT]
@property
def output_devices(self):
"""Return list of ALSA output devices."""
self._update_device()
return self._data[ATTR_OUTPUT]
def _update_device(self):
"""Update Internal device DB."""
current_id = hash(frozenset(self.sys_hardware.audio_devices))
# Need rebuild?
if current_id == self._cache:
return
# Clean old stuff
self._data[ATTR_INPUT].clear()
self._data[ATTR_OUTPUT].clear()
# Init database
_LOGGER.info("Update ALSA device list")
database = self._audio_database()
# Process devices
for dev_id, dev_data in self.sys_hardware.audio_devices.items():
for chan_info in dev_data[ATTR_DEVICES]:
chan_id = chan_info[CHAN_ID]
chan_type = chan_info[CHAN_TYPE]
alsa_id = f"{dev_id},{chan_id}"
dev_name = dev_data[ATTR_NAME]
# Lookup type
if chan_type.endswith("playback"):
key = ATTR_OUTPUT
elif chan_type.endswith("capture"):
key = ATTR_INPUT
else:
_LOGGER.warning("Unknown channel type: %s", chan_type)
continue
# Use name from DB or a generic name
self._data[key][alsa_id] = (
database.get(self.sys_machine, {})
.get(dev_name, {})
.get(alsa_id, f"{dev_name}: {chan_id}")
)
self._cache = current_id
@staticmethod
def _audio_database():
"""Read local json audio data into dict."""
try:
return json.loads(AUDIODB_JSON.read_text())
except (ValueError, OSError) as err:
_LOGGER.warning("Can't read audio DB: %s", err)
return {}
@property
def default(self):
"""Generate ALSA default setting."""
# Init defaults
if self._default is None:
database = self._audio_database()
alsa_input = database.get(self.sys_machine, {}).get(ATTR_INPUT)
alsa_output = database.get(self.sys_machine, {}).get(ATTR_OUTPUT)
self._default = DefaultConfig(alsa_input, alsa_output)
# Search exists/new output
if self._default.output is None and self.output_devices:
self._default.output = next(iter(self.output_devices))
_LOGGER.info("Detect output device %s", self._default.output)
# Search exists/new input
if self._default.input is None and self.input_devices:
self._default.input = next(iter(self.input_devices))
_LOGGER.info("Detect input device %s", self._default.input)
return self._default
def asound(self, alsa_input=None, alsa_output=None):
"""Generate an asound data."""
alsa_input = alsa_input or self.default.input
alsa_output = alsa_output or self.default.output
# Read Template
try:
asound_data = ASOUND_TMPL.read_text()
except OSError as err:
_LOGGER.error("Can't read asound.tmpl: %s", err)
return ""
# Process Template
asound_template = Template(asound_data)
return asound_template.safe_substitute(input=alsa_input, output=alsa_output)

View File

@@ -1 +0,0 @@
"""Special object and tools for Hass.io."""

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -1 +0,0 @@
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" version="7.9.5" editor="www.draw.io" type="device"><diagram name="Page-1" id="535f6c39-9b73-04c2-941c-82630de90f1a">5VrLcqM4FP0aLzsFiOcycefRVTPVqfFippdYKLYmMvII4cd8/QiQDEKQ4Bicnmp7Yevqybk691zJnoH55vDI4u36d5ogMnOs5DADX2eOY1vAER+F5VhZ3DCoDCuGE9moNizwv0j1lNYcJyjTGnJKCcdb3QhpmiLINVvMGN3rzV4o0WfdxitkGBYwJqb1T5zwtbTaflRXPCG8WsupQ8evKpYxfF0xmqdyvpkDXspXVb2J1VjyQbN1nNB9wwTuZ2DOKOXVt81hjkiBrYKt6vfQU3taN0MpH9JB+mkXkxypFftEdL17oWIEsUB+lKD4/+RUVXzJSpfdigZitoP4EBUl0KJuL4EpalPKNjGpO4tvq+Lz+0LNI9ZWTVVVSFhOszr7NeZosY1hUd6L7SYarfmGiJJdrAYTMqeEsrK1ABv5EAp7xhl9RY0aq3zJ9S/k+B14SdMOMY4ODZPE7xHRDeLsKJqo2ghUXeRe9yLp2329c1wF9LqxaXzZLpabdXUaunaY+CJ91u0/YPjvW4oLvy2OGUebC9GECVqGyy40gQ8ikJz8NS6AwUAAnREAdA0Av1L4itilyEHkCdJfGznXRM7pQg6MgJxvIPc0N1ArQyEqehTUO5PLIUTdXF6GnutZ42Do+p6OoW9i6FkmhN4IEEYGXigROiSLlPE1XdE0Jve19U5HtIEeOmD+V2G+CTxZ/KGqUrGwqs5TxR9yhL8R50epwHHOqTDVE/9G6VaO0Qt1RnMG5fKlyvOYrRDXtknxYG+6gyESc7zTBfgScFUuMTa6zhvoRiLxaeFbFp4Rw+IBELsS6O5ngR705hPLWuHPSzBsv0gw2gnEIt8itsOZCAlqAqbqnuIs+/a9N8E4mZe9SUe9Dez3w5YRnuZz369SDT2gJR4KE3ecsAU8PWyBjqzDDjvilj2GatrOFNyyG8RSUezELY1XZRgbSqJMMIPfFqcCYYBEbA4MlfkBE7WKQVyz1WmkQbbgs8gGpolwmhd0J7Tkoy62A9xAzIe6EKWJOZgwNobqTPjn80sc64Sfpl0qHjSSKzHKl1vx6ALDIppdJ2LFKHyBYyWresRyOtL8U3DS0nx3jIjlX5kr9o2l5wI3dhhemg8MpFWDLilNkcaVN9NmjRHAZITal9dnhDuJ4kifNZK5kRAe7tC+awqYs92Jzx922Kdpk2veTHzAgRoIvd4832d9InK52zrx/rjrrqE1pqduk4SmmeGvbB1vi69bRiHKsvd1RhelwarzIF6lcleHAMFSy/EDEDnA90InDC0XTJRFd2mSY3umJkUjSJK6vJsypNWltuRcmtTJsNck2Sgn2/FClez6THF50JQuV2ei9rlJjVDRUnZyGjfnZ45TUdkYp9wUp6cZtk9Ck6CQU/OKUvEz35CqAbgrqIChQD5eIvJMM8wxTUWTJeWcbkQDUlTcnX610K7Sy98t6jFuCV4VfTk9j+b1zXv7rl5OMAKRW5d4oOMSD3SklqNcwZs0HkBSK9BY6r7HUtvk6BA6XkXzztTxQYqofkH8KZIZtZgGA/f7vRm9CcHbrHSDZCIkNE8u1smrECjS45lrdZzOgqnuk8DbN+Fyc3/gOHYmRybK5RtaW58Bq0U6vWo7jCauSRO1WydXUre1ZdrRdDwJBP0/01lP+bJXCWHMLqefX7466OcV73HoF4FWOtFFv67r3FEULJiIfc19H4yZZU5P2WHs867BvsFu9AySPGK+npoefeqE7MRDwTT0cNWh9Sr0CH8VcYp8naPBZdrk/xraZP4R4g+0LY5alGHUf4vy/yWfusifgHyiWP/5rXJG/Q9DcP8f</diagram></mxfile>

View File

@@ -6,11 +6,13 @@ colorlog==4.1.0
cpe==1.2.1
cryptography==2.8
docker==4.2.0
gitpython==3.0.7
gitpython==3.1.0
jinja2==2.11.1
packaging==20.1
ptvsd==4.3.2
pulsectl==20.2.4
pytz==2019.3
pyudev==0.22.0
ruamel.yaml==0.15.100
uvloop==0.14.0
voluptuous==0.11.7
ptvsd==4.3.2

View File

@@ -0,0 +1,9 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Start udev service
# ==============================================================================
udevd --daemon
bashio::log.info "Update udev informations"
udevadm trigger
udevadm settle

View File

@@ -0,0 +1,35 @@
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
## Configuration file for PulseAudio clients. See pulse-client.conf(5) for
## more information. Default values are commented out. Use either ; or # for
## commenting.
; default-sink =
; default-source =
default-server = unix://data/audio/external/pulse.sock
; default-dbus-server =
autospawn = no
; daemon-binary = /usr/bin/pulseaudio
; extra-arguments = --log-target=syslog
; cookie-file =
; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
; auto-connect-localhost = no
; auto-connect-display = no

View File

@@ -0,0 +1,5 @@
#!/usr/bin/execlineb -S0
# ==============================================================================
# Take down the S6 supervision tree when Supervisor fails
# ==============================================================================
s6-svscanctl -t /var/run/s6/services

View File

@@ -0,0 +1,5 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Start Service service
# ==============================================================================
exec python3 -m supervisor

View File

@@ -61,9 +61,7 @@ function build_supervisor() {
docker run --rm --privileged \
-v /run/docker.sock:/run/docker.sock -v "$(pwd):/data" \
homeassistant/amd64-builder:dev \
--supervisor 3.7-alpine3.11 --version dev \
-t /data --test --amd64 \
--no-cache --docker-hub homeassistant
--generic dev -t /data --test --amd64 --no-cache
}
@@ -79,7 +77,7 @@ function cleanup_lastboot() {
function cleanup_docker() {
echo "Cleaning up stopped containers..."
docker rm $(docker ps -a -q)
docker rm $(docker ps -a -q) || true
}
@@ -108,6 +106,25 @@ function setup_test_env() {
}
function init_dbus() {
if pgrep dbus-daemon; then
echo "Dbus is running"
return 0
fi
echo "Startup dbus"
mkdir -p /var/lib/dbus
cp -f /etc/machine-id /var/lib/dbus/machine-id
# cleanups
mkdir -p /run/dbus
rm -f /run/dbus/pid
# run
dbus-daemon --system --print-address
}
echo "Start Test-Env"
start_docker
@@ -117,5 +134,6 @@ build_supervisor
install_cli
cleanup_lastboot
cleanup_docker
init_dbus
setup_test_env
stop_docker

View File

@@ -14,5 +14,5 @@ cd hassio
./script/build_hassio
# Copy frontend
rm -f ../../hassio/api/panel/chunk.*
cp -rf build/* ../../hassio/api/panel/
rm -f ../../supervisor/hassio/api/panel/chunk.*
cp -rf build/* ../../supervisor/api/panel/

View File

@@ -1,10 +1,11 @@
"""Home Assistant Supervisor setup."""
from setuptools import setup
from hassio.const import HASSIO_VERSION
from supervisor.const import SUPERVISOR_VERSION
setup(
name="HassIO",
version=HASSIO_VERSION,
name="Supervisor",
version=SUPERVISOR_VERSION,
license="BSD License",
author="The Home Assistant Authors",
author_email="hello@home-assistant.io",
@@ -24,19 +25,19 @@ setup(
"Topic :: Scientific/Engineering :: Atmospheric Science",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
],
keywords=["docker", "home-assistant", "api"],
zip_safe=False,
platforms="any",
packages=[
"hassio",
"hassio.docker",
"hassio.addons",
"hassio.api",
"hassio.misc",
"hassio.utils",
"hassio.snapshots",
"supervisor",
"supervisor.docker",
"supervisor.addons",
"supervisor.api",
"supervisor.misc",
"supervisor.utils",
"supervisor.snapshots",
],
include_package_data=True,
)

1
supervisor/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Init file for Supervisor."""

View File

@@ -1,10 +1,10 @@
"""Main file for Hass.io."""
"""Main file for Supervisor."""
import asyncio
from concurrent.futures import ThreadPoolExecutor
import logging
import sys
from hassio import bootstrap
from supervisor import bootstrap
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -29,7 +29,7 @@ if __name__ == "__main__":
# Init async event loop
loop = initialize_event_loop()
# Check if all information are available to setup Hass.io
# Check if all information are available to setup Supervisor
if not bootstrap.check_environment():
sys.exit(1)
@@ -37,27 +37,27 @@ if __name__ == "__main__":
executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker")
loop.set_default_executor(executor)
_LOGGER.info("Initialize Hass.io setup")
_LOGGER.info("Initialize Supervisor setup")
coresys = loop.run_until_complete(bootstrap.initialize_coresys())
loop.run_until_complete(coresys.core.connect())
bootstrap.supervisor_debugger(coresys)
bootstrap.migrate_system_env(coresys)
_LOGGER.info("Setup HassIO")
_LOGGER.info("Setup Supervisor")
loop.run_until_complete(coresys.core.setup())
loop.call_soon_threadsafe(loop.create_task, coresys.core.start())
loop.call_soon_threadsafe(bootstrap.reg_signal, loop)
try:
_LOGGER.info("Run Hass.io")
_LOGGER.info("Run Supervisor")
loop.run_forever()
finally:
_LOGGER.info("Stopping Hass.io")
_LOGGER.info("Stopping Supervisor")
loop.run_until_complete(coresys.core.stop())
executor.shutdown(wait=False)
loop.close()
_LOGGER.info("Close Hass.io")
_LOGGER.info("Close Supervisor")
sys.exit(0)

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io add-ons."""
"""Init file for Supervisor add-ons."""
import asyncio
from contextlib import suppress
import logging
@@ -25,7 +25,7 @@ AnyAddon = Union[Addon, AddonStore]
class AddonManager(CoreSysAttributes):
"""Manage add-ons inside Hass.io."""
"""Manage add-ons inside Supervisor."""
def __init__(self, coresys: CoreSys):
"""Initialize Docker base wrapper."""
@@ -57,7 +57,7 @@ class AddonManager(CoreSysAttributes):
return self.store.get(addon_slug)
def from_token(self, token: str) -> Optional[Addon]:
"""Return an add-on from Hass.io token."""
"""Return an add-on from Supervisor token."""
for addon in self.installed:
if token == addon.hassio_token:
return addon
@@ -152,9 +152,9 @@ class AddonManager(CoreSysAttributes):
await addon.remove_data()
# Cleanup audio settings
if addon.path_asound.exists():
if addon.path_pulse.exists():
with suppress(OSError):
addon.path_asound.unlink()
addon.path_pulse.unlink()
# Cleanup AppArmor profile
with suppress(HostAppArmorError):

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io add-ons."""
"""Init file for Supervisor add-ons."""
from contextlib import suppress
from copy import deepcopy
from ipaddress import IPv4Address
@@ -63,9 +63,11 @@ RE_WEBUI = re.compile(
r":\/\/\[HOST\]:\[PORT:(?P<t_port>\d+)\](?P<s_suffix>.*)$"
)
RE_OLD_AUDIO = re.compile(r"\d+,\d+")
class Addon(AddonModel):
"""Hold data for add-on inside Hass.io."""
"""Hold data for add-on inside Supervisor."""
def __init__(self, coresys: CoreSys, slug: str):
"""Initialize data holder."""
@@ -163,12 +165,12 @@ class Addon(AddonModel):
@property
def hassio_token(self) -> Optional[str]:
"""Return access token for Hass.io API."""
"""Return access token for Supervisor API."""
return self.persist.get(ATTR_ACCESS_TOKEN)
@property
def ingress_token(self) -> Optional[str]:
"""Return access token for Hass.io API."""
"""Return access token for Supervisor API."""
return self.persist.get(ATTR_INGRESS_TOKEN)
@property
@@ -250,7 +252,7 @@ class Addon(AddonModel):
# lookup the correct protocol from config
if t_proto:
proto = "https" if self.options[t_proto] else "http"
proto = "https" if self.options.get(t_proto) else "http"
else:
proto = s_prefix
@@ -279,14 +281,20 @@ class Addon(AddonModel):
@property
def audio_output(self) -> Optional[str]:
"""Return ALSA config for output or None."""
"""Return a pulse profile for output or None."""
if not self.with_audio:
return None
return self.persist.get(ATTR_AUDIO_OUTPUT, self.sys_host.alsa.default.output)
# Fallback with old audio settings
# Remove after 210
output_data = self.persist.get(ATTR_AUDIO_OUTPUT)
if output_data and RE_OLD_AUDIO.fullmatch(output_data):
return None
return output_data
@audio_output.setter
def audio_output(self, value: Optional[str]):
"""Set/reset audio output settings."""
"""Set/reset audio output profile settings."""
if value is None:
self.persist.pop(ATTR_AUDIO_OUTPUT, None)
else:
@@ -294,10 +302,16 @@ class Addon(AddonModel):
@property
def audio_input(self) -> Optional[str]:
"""Return ALSA config for input or None."""
"""Return pulse profile for input or None."""
if not self.with_audio:
return None
return self.persist.get(ATTR_AUDIO_INPUT, self.sys_host.alsa.default.input)
# Fallback with old audio settings
# Remove after 210
input_data = self.persist.get(ATTR_AUDIO_INPUT)
if input_data and RE_OLD_AUDIO.fullmatch(input_data):
return None
return input_data
@audio_input.setter
def audio_input(self, value: Optional[str]):
@@ -333,14 +347,14 @@ class Addon(AddonModel):
return Path(self.path_data, "options.json")
@property
def path_asound(self):
def path_pulse(self):
"""Return path to asound config."""
return Path(self.sys_config.path_tmp, f"{self.slug}_asound")
return Path(self.sys_config.path_tmp, f"{self.slug}_pulse")
@property
def path_extern_asound(self):
def path_extern_pulse(self):
"""Return path to asound config for Docker."""
return Path(self.sys_config.path_extern_tmp, f"{self.slug}_asound")
return Path(self.sys_config.path_extern_tmp, f"{self.slug}_pulse")
def save_persist(self):
"""Save data of add-on."""
@@ -379,20 +393,24 @@ class Addon(AddonModel):
_LOGGER.info("Remove add-on data folder %s", self.path_data)
await remove_data(self.path_data)
def write_asound(self):
def write_pulse(self):
"""Write asound config to file and return True on success."""
asound_config = self.sys_host.alsa.asound(
alsa_input=self.audio_input, alsa_output=self.audio_output
pulse_config = self.sys_audio.pulse_client(
input_profile=self.audio_input, output_profile=self.audio_output
)
try:
with self.path_asound.open("w") as config_file:
config_file.write(asound_config)
with self.path_pulse.open("w") as config_file:
config_file.write(pulse_config)
except OSError as err:
_LOGGER.error("Add-on %s can't write asound: %s", self.slug, err)
_LOGGER.error(
"Add-on %s can't write pulse/client.config: %s", self.slug, err
)
raise AddonsError()
_LOGGER.debug("Add-on %s write asound: %s", self.slug, self.path_asound)
_LOGGER.debug(
"Add-on %s write pulse/client.config: %s", self.slug, self.path_pulse
)
async def install_apparmor(self) -> None:
"""Install or Update AppArmor profile for Add-on."""
@@ -468,7 +486,7 @@ class Addon(AddonModel):
# Sound
if self.with_audio:
self.write_asound()
self.write_pulse()
# Start Add-on
try:

View File

@@ -1,4 +1,4 @@
"""Hass.io add-on build environment."""
"""Supervisor add-on build environment."""
from __future__ import annotations
from pathlib import Path
from typing import TYPE_CHECKING, Dict
@@ -16,7 +16,7 @@ class AddonBuild(JsonConfig, CoreSysAttributes):
"""Handle build options for add-ons."""
def __init__(self, coresys: CoreSys, addon: AnyAddon) -> None:
"""Initialize Hass.io add-on builder."""
"""Initialize Supervisor add-on builder."""
self.coresys: CoreSys = coresys
self.addon = addon

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io add-on data."""
"""Init file for Supervisor add-on data."""
from copy import deepcopy
import logging
from typing import Any, Dict
@@ -23,7 +23,7 @@ Config = Dict[str, Any]
class AddonsData(JsonConfig, CoreSysAttributes):
"""Hold data for installed Add-ons inside Hass.io."""
"""Hold data for installed Add-ons inside Supervisor."""
def __init__(self, coresys: CoreSys):
"""Initialize data holder."""

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io add-ons."""
"""Init file for Supervisor add-ons."""
from pathlib import Path
from typing import Any, Awaitable, Dict, List, Optional
@@ -31,6 +31,7 @@ from ..const import (
ATTR_HOST_PID,
ATTR_IMAGE,
ATTR_INGRESS,
ATTR_INIT,
ATTR_KERNEL_MODULES,
ATTR_LEGACY,
ATTR_LOCATON,
@@ -137,12 +138,12 @@ class AddonModel(CoreSysAttributes):
@property
def hassio_token(self) -> Optional[str]:
"""Return access token for Hass.io API."""
"""Return access token for Supervisor API."""
return None
@property
def ingress_token(self) -> Optional[str]:
"""Return access token for Hass.io API."""
"""Return access token for Supervisor API."""
return None
@property
@@ -326,7 +327,7 @@ class AddonModel(CoreSysAttributes):
@property
def access_hassio_api(self) -> bool:
"""Return True if the add-on access to Hass.io REASTful API."""
"""Return True if the add-on access to Supervisor REASTful API."""
return self.data[ATTR_HASSIO_API]
@property
@@ -336,7 +337,7 @@ class AddonModel(CoreSysAttributes):
@property
def hassio_role(self) -> str:
"""Return Hass.io role for API."""
"""Return Supervisor role for API."""
return self.data[ATTR_HASSIO_ROLE]
@property
@@ -344,6 +345,11 @@ class AddonModel(CoreSysAttributes):
"""Return Exclude list for snapshot."""
return self.data.get(ATTR_SNAPSHOT_EXCLUDE, [])
@property
def default_init(self) -> bool:
"""Return True if the add-on have no own init."""
return self.data[ATTR_INIT]
@property
def with_stdin(self) -> bool:
"""Return True if the add-on access use stdin input."""

View File

@@ -59,7 +59,7 @@ def rating_security(addon: AddonModel) -> int:
):
rating += -1
# API Hass.io role
# API Supervisor role
if addon.hassio_role == ROLE_MANAGER:
rating += -1
elif addon.hassio_role == ROLE_ADMIN:

View File

@@ -44,6 +44,7 @@ from ..const import (
ATTR_INGRESS_PANEL,
ATTR_INGRESS_PORT,
ATTR_INGRESS_TOKEN,
ATTR_INIT,
ATTR_KERNEL_MODULES,
ATTR_LEGACY,
ATTR_LOCATON,
@@ -96,7 +97,6 @@ from ..discovery.validate import valid_discovery_service
from ..validate import (
DOCKER_PORTS,
DOCKER_PORTS_DESCRIPTION,
alsa_device,
network_port,
token,
uuid_match,
@@ -190,6 +190,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Optional(ATTR_URL): vol.Url(),
vol.Required(ATTR_STARTUP): vol.All(_simple_startup, vol.In(STARTUP_ALL)),
vol.Required(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_INIT, default=True): vol.Boolean(),
vol.Optional(ATTR_ADVANCED, default=False): vol.Boolean(),
vol.Optional(ATTR_STAGE, default=AddonStages.STABLE): vol.Coerce(AddonStages),
vol.Optional(ATTR_PORTS): DOCKER_PORTS,
@@ -296,8 +297,8 @@ SCHEMA_ADDON_USER = vol.Schema(
vol.Optional(ATTR_AUTO_UPDATE, default=False): vol.Boolean(),
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_NETWORK): DOCKER_PORTS,
vol.Optional(ATTR_AUDIO_OUTPUT): alsa_device,
vol.Optional(ATTR_AUDIO_INPUT): alsa_device,
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(),
vol.Optional(ATTR_INGRESS_PANEL, default=False): vol.Boolean(),
},

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io RESTful API."""
"""Init file for Supervisor RESTful API."""
import logging
from pathlib import Path
from typing import Optional
@@ -21,6 +21,7 @@ from .security import SecurityMiddleware
from .services import APIServices
from .snapshots import APISnapshots
from .supervisor import APISupervisor
from .audio import APIAudio
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -29,7 +30,7 @@ MAX_CLIENT_SIZE: int = 1024 ** 2 * 16
class RestAPI(CoreSysAttributes):
"""Handle RESTful API for Hass.io."""
"""Handle RESTful API for Supervisor."""
def __init__(self, coresys: CoreSys):
"""Initialize Docker base wrapper."""
@@ -61,6 +62,7 @@ class RestAPI(CoreSysAttributes):
self._register_info()
self._register_auth()
self._register_dns()
self._register_audio()
def _register_host(self) -> None:
"""Register hostcontrol functions."""
@@ -93,7 +95,7 @@ class RestAPI(CoreSysAttributes):
web.post("/os/update", api_hassos.update),
web.post("/os/update/cli", api_hassos.update_cli),
web.post("/os/config/sync", api_hassos.config_sync),
# Remove with old Hass.io fallback
# Remove with old Supervisor fallback
web.get("/hassos/info", api_hassos.info),
web.post("/hassos/update", api_hassos.update),
web.post("/hassos/update/cli", api_hassos.update_cli),
@@ -165,7 +167,7 @@ class RestAPI(CoreSysAttributes):
web.post("/core/start", api_hass.start),
web.post("/core/check", api_hass.check),
web.post("/core/rebuild", api_hass.rebuild),
# Remove with old Hass.io fallback
# Remove with old Supervisor fallback
web.get("/homeassistant/info", api_hass.info),
web.get("/homeassistant/logs", api_hass.logs),
web.get("/homeassistant/stats", api_hass.stats),
@@ -192,7 +194,7 @@ class RestAPI(CoreSysAttributes):
web.post("/core/api/{path:.+}", api_proxy.api),
web.get("/core/api/{path:.+}", api_proxy.api),
web.get("/core/api/", api_proxy.api),
# Remove with old Hass.io fallback
# Remove with old Supervisor fallback
web.get("/homeassistant/api/websocket", api_proxy.websocket),
web.get("/homeassistant/websocket", api_proxy.websocket),
web.get("/homeassistant/api/stream", api_proxy.stream),
@@ -314,6 +316,28 @@ class RestAPI(CoreSysAttributes):
]
)
def _register_audio(self) -> None:
"""Register Audio functions."""
api_audio = APIAudio()
api_audio.coresys = self.coresys
self.webapp.add_routes(
[
web.get("/audio/info", api_audio.info),
web.get("/audio/stats", api_audio.stats),
web.get("/audio/logs", api_audio.logs),
web.post("/audio/update", api_audio.update),
web.post("/audio/restart", api_audio.restart),
web.post("/audio/reload", api_audio.reload),
web.post("/audio/profile", api_audio.set_profile),
web.post("/audio/volume/{source}/application", api_audio.set_volume),
web.post("/audio/volume/{source}", api_audio.set_volume),
web.post("/audio/mute/{source}/application", api_audio.set_mute),
web.post("/audio/mute/{source}", api_audio.set_mute),
web.post("/audio/default/{source}", api_audio.set_default),
]
)
def _register_panel(self) -> None:
"""Register panel for Home Assistant."""
panel_dir = Path(__file__).parent.joinpath("panel")

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io Home Assistant RESTful API."""
"""Init file for Supervisor Home Assistant RESTful API."""
import asyncio
import logging
from typing import Any, Awaitable, Dict, List
@@ -96,7 +96,7 @@ from ..const import (
from ..coresys import CoreSysAttributes
from ..docker.stats import DockerStats
from ..exceptions import APIError
from ..validate import DOCKER_PORTS, alsa_device
from ..validate import DOCKER_PORTS
from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -107,10 +107,10 @@ SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
SCHEMA_OPTIONS = vol.Schema(
{
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_NETWORK): vol.Any(None, DOCKER_PORTS),
vol.Optional(ATTR_NETWORK): vol.Maybe(DOCKER_PORTS),
vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(),
vol.Optional(ATTR_AUDIO_OUTPUT): alsa_device,
vol.Optional(ATTR_AUDIO_INPUT): alsa_device,
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_INGRESS_PANEL): vol.Boolean(),
}
)

170
supervisor/api/audio.py Normal file
View File

@@ -0,0 +1,170 @@
"""Init file for Supervisor Audio RESTful API."""
import asyncio
import logging
from typing import Any, Awaitable, Dict
from aiohttp import web
import attr
import voluptuous as vol
from ..const import (
ATTR_ACTIVE,
ATTR_APPLICATION,
ATTR_AUDIO,
ATTR_BLK_READ,
ATTR_BLK_WRITE,
ATTR_CARD,
ATTR_CPU_PERCENT,
ATTR_HOST,
ATTR_INDEX,
ATTR_INPUT,
ATTR_LATEST_VERSION,
ATTR_MEMORY_LIMIT,
ATTR_MEMORY_PERCENT,
ATTR_MEMORY_USAGE,
ATTR_NAME,
ATTR_NETWORK_RX,
ATTR_NETWORK_TX,
ATTR_OUTPUT,
ATTR_VERSION,
ATTR_VOLUME,
CONTENT_TYPE_BINARY,
)
from ..coresys import CoreSysAttributes
from ..exceptions import APIError
from ..host.sound import StreamType
from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__)
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
SCHEMA_VOLUME = vol.Schema(
{
vol.Required(ATTR_INDEX): vol.Coerce(int),
vol.Required(ATTR_VOLUME): vol.Coerce(float),
}
)
# pylint: disable=no-value-for-parameter
SCHEMA_MUTE = vol.Schema(
{
vol.Required(ATTR_INDEX): vol.Coerce(int),
vol.Required(ATTR_ACTIVE): vol.Boolean(),
}
)
SCHEMA_DEFAULT = vol.Schema({vol.Required(ATTR_NAME): vol.Coerce(str)})
SCHEMA_PROFILE = vol.Schema(
{vol.Required(ATTR_CARD): vol.Coerce(str), vol.Required(ATTR_NAME): vol.Coerce(str)}
)
class APIAudio(CoreSysAttributes):
"""Handle RESTful API for Audio functions."""
@api_process
async def info(self, request: web.Request) -> Dict[str, Any]:
"""Return Audio information."""
return {
ATTR_VERSION: self.sys_audio.version,
ATTR_LATEST_VERSION: self.sys_audio.latest_version,
ATTR_HOST: str(self.sys_docker.network.audio),
ATTR_AUDIO: {
ATTR_CARD: [attr.asdict(card) for card in self.sys_host.sound.cards],
ATTR_INPUT: [
attr.asdict(stream) for stream in self.sys_host.sound.inputs
],
ATTR_OUTPUT: [
attr.asdict(stream) for stream in self.sys_host.sound.outputs
],
ATTR_APPLICATION: [
attr.asdict(stream) for stream in self.sys_host.sound.applications
],
},
}
@api_process
async def stats(self, request: web.Request) -> Dict[str, Any]:
"""Return resource information."""
stats = await self.sys_audio.stats()
return {
ATTR_CPU_PERCENT: stats.cpu_percent,
ATTR_MEMORY_USAGE: stats.memory_usage,
ATTR_MEMORY_LIMIT: stats.memory_limit,
ATTR_MEMORY_PERCENT: stats.memory_percent,
ATTR_NETWORK_RX: stats.network_rx,
ATTR_NETWORK_TX: stats.network_tx,
ATTR_BLK_READ: stats.blk_read,
ATTR_BLK_WRITE: stats.blk_write,
}
@api_process
async def update(self, request: web.Request) -> None:
"""Update Audio plugin."""
body = await api_validate(SCHEMA_VERSION, request)
version = body.get(ATTR_VERSION, self.sys_audio.latest_version)
if version == self.sys_audio.version:
raise APIError("Version {} is already in use".format(version))
await asyncio.shield(self.sys_audio.update(version))
@api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request: web.Request) -> Awaitable[bytes]:
"""Return Audio Docker logs."""
return self.sys_audio.logs()
@api_process
def restart(self, request: web.Request) -> Awaitable[None]:
"""Restart Audio plugin."""
return asyncio.shield(self.sys_audio.restart())
@api_process
def reload(self, request: web.Request) -> Awaitable[None]:
"""Reload Audio information."""
return asyncio.shield(self.sys_host.sound.update())
@api_process
async def set_volume(self, request: web.Request) -> None:
"""Set audio volume on stream."""
source: StreamType = StreamType(request.match_info.get("source"))
application: bool = request.path.endswith("application")
body = await api_validate(SCHEMA_VOLUME, request)
await asyncio.shield(
self.sys_host.sound.set_volume(
source, body[ATTR_INDEX], body[ATTR_VOLUME], application
)
)
@api_process
async def set_mute(self, request: web.Request) -> None:
"""Mute audio volume on stream."""
source: StreamType = StreamType(request.match_info.get("source"))
application: bool = request.path.endswith("application")
body = await api_validate(SCHEMA_MUTE, request)
await asyncio.shield(
self.sys_host.sound.set_mute(
source, body[ATTR_INDEX], body[ATTR_ACTIVE], application
)
)
@api_process
async def set_default(self, request: web.Request) -> None:
"""Set audio default stream."""
source: StreamType = StreamType(request.match_info.get("source"))
body = await api_validate(SCHEMA_DEFAULT, request)
await asyncio.shield(self.sys_host.sound.set_default(source, body[ATTR_NAME]))
@api_process
async def set_profile(self, request: web.Request) -> None:
"""Set audio default sources."""
body = await api_validate(SCHEMA_DEFAULT, request)
await asyncio.shield(
self.sys_host.sound.set_profile(body[ATTR_CARD], body[ATTR_NAME])
)

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io auth/SSO RESTful API."""
"""Init file for Supervisor auth/SSO RESTful API."""
import asyncio
import logging
from typing import Dict
@@ -76,7 +76,7 @@ class APIAuth(CoreSysAttributes):
return await self._process_dict(request, addon, data)
raise HTTPUnauthorized(
headers={WWW_AUTHENTICATE: 'Basic realm="Hass.io Authentication"'}
headers={WWW_AUTHENTICATE: 'Basic realm="Home Assistant Authentication"'}
)
@api_process

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io network RESTful API."""
"""Init file for Supervisor network RESTful API."""
import voluptuous as vol
from .utils import api_process, api_validate

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io DNS RESTful API."""
"""Init file for Supervisor DNS RESTful API."""
import asyncio
import logging
from typing import Any, Awaitable, Dict

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io hardware RESTful API."""
"""Init file for Supervisor hardware RESTful API."""
import asyncio
import logging
from typing import Any, Dict
@@ -37,11 +37,17 @@ class APIHardware(CoreSysAttributes):
@api_process
async def audio(self, request: web.Request) -> Dict[str, Any]:
"""Show ALSA audio devices."""
"""Show pulse audio profiles."""
return {
ATTR_AUDIO: {
ATTR_INPUT: self.sys_host.alsa.input_devices,
ATTR_OUTPUT: self.sys_host.alsa.output_devices,
ATTR_INPUT: {
profile.name: profile.description
for profile in self.sys_host.sound.inputs
},
ATTR_OUTPUT: {
profile.name: profile.description
for profile in self.sys_host.sound.outputs
},
}
}

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io HassOS RESTful API."""
"""Init file for Supervisor HassOS RESTful API."""
import asyncio
import logging
from typing import Any, Awaitable, Dict

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io Home Assistant RESTful API."""
"""Init file for Supervisor Home Assistant RESTful API."""
import asyncio
import logging
from typing import Coroutine, Dict, Any

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io host RESTful API."""
"""Init file for Supervisor host RESTful API."""
import asyncio
import logging

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io info RESTful API."""
"""Init file for Supervisor info RESTful API."""
import logging
from typing import Any, Dict

View File

@@ -1,4 +1,4 @@
"""Hass.io Add-on ingress service."""
"""Supervisor Add-on ingress service."""
import asyncio
from ipaddress import ip_address
import logging
@@ -81,7 +81,7 @@ class APIIngress(CoreSysAttributes):
async def handler(
self, request: web.Request
) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]:
"""Route data to Hass.io ingress service."""
"""Route data to Supervisor ingress service."""
self._check_ha_access(request)
# Check Ingress Session

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

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,2 @@
(self.webpackJsonp=self.webpackJsonp||[]).push([[2],{177:function(e,r,n){"use strict";n.r(r),n.d(r,"codeMirror",function(){return c}),n.d(r,"codeMirrorCss",function(){return i});var a=n(54),o=n.n(a),s=n(170),t=(n(171),n(172),n(11));o.a.commands.save=function(e){Object(t.a)(e.getWrapperElement(),"editor-save")};var c=o.a,i=s.a}}]);
//# sourceMappingURL=chunk.26756b56961f7bf94974.js.map

Binary file not shown.

View File

@@ -0,0 +1 @@
{"version":3,"sources":["webpack:///./src/resources/codemirror.ts"],"names":["__webpack_require__","r","__webpack_exports__","d","codeMirror","codeMirrorCss","codemirror__WEBPACK_IMPORTED_MODULE_0__","codemirror__WEBPACK_IMPORTED_MODULE_0___default","n","codemirror_lib_codemirror_css__WEBPACK_IMPORTED_MODULE_1__","_common_dom_fire_event__WEBPACK_IMPORTED_MODULE_4__","_CodeMirror","commands","save","cm","fireEvent","getWrapperElement","_codeMirrorCss"],"mappings":"sFAAAA,EAAAC,EAAAC,GAAAF,EAAAG,EAAAD,EAAA,+BAAAE,IAAAJ,EAAAG,EAAAD,EAAA,kCAAAG,IAAA,IAAAC,EAAAN,EAAA,IAAAO,EAAAP,EAAAQ,EAAAF,GAAAG,EAAAT,EAAA,KAAAU,GAAAV,EAAA,KAAAA,EAAA,KAAAA,EAAA,KAQAW,IAAYC,SAASC,KAAO,SAACC,GAC3BC,YAAUD,EAAGE,oBAAqB,gBAE7B,IAAMZ,EAAkBO,IAClBN,EAAqBY","file":"chunk.26756b56961f7bf94974.js","sourcesContent":["// @ts-ignore\nimport _CodeMirror, { Editor } from \"codemirror\";\n// @ts-ignore\nimport _codeMirrorCss from \"codemirror/lib/codemirror.css\";\nimport \"codemirror/mode/yaml/yaml\";\nimport \"codemirror/mode/jinja2/jinja2\";\nimport { fireEvent } from \"../common/dom/fire_event\";\n\n_CodeMirror.commands.save = (cm: Editor) => {\n fireEvent(cm.getWrapperElement(), \"editor-save\");\n};\nexport const codeMirror: any = _CodeMirror;\nexport const codeMirrorCss: any = _codeMirrorCss;\n"],"sourceRoot":""}

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

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1 @@
{"version":3,"sources":["webpack:///./hassio/src/ingress-view/hassio-ingress-view.ts"],"names":["customElement","HassioIngressView","property","this","_addon","html","_templateObject2","name","ingress_url","_templateObject","changedProps","_get","_getPrototypeOf","prototype","call","has","addon","route","path","substr","oldRoute","get","oldAddon","undefined","_fetchData","_callee","addonSlug","_ref","_ref2","regeneratorRuntime","wrap","_context","prev","next","Promise","all","fetchHassioAddonInfo","hass","Error","createHassioSession","sent","_slicedToArray","ingress","t0","console","error","alert","message","history","back","stop","css","_templateObject3","LitElement"],"mappings":"snSAmBCA,YAAc,0CACTC,smBACHC,kEACAA,mEACAA,4EAED,WACE,OAAKC,KAAKC,OAMHC,YAAPC,IAC0BH,KAAKC,OAAOG,KACpBJ,KAAKC,OAAOI,aAPrBH,YAAPI,0CAYJ,SAAkBC,GAGhB,GAFAC,EAAAC,EApBEX,EAoBFY,WAAA,eAAAV,MAAAW,KAAAX,KAAmBO,GAEdA,EAAaK,IAAI,SAAtB,CAIA,IAAMC,EAAQb,KAAKc,MAAMC,KAAKC,OAAO,GAE/BC,EAAWV,EAAaW,IAAI,SAC5BC,EAAWF,EAAWA,EAASF,KAAKC,OAAO,QAAKI,EAElDP,GAASA,IAAUM,GACrBnB,KAAKqB,WAAWR,0FAIpB,SAAAS,EAAyBC,GAAzB,IAAAC,EAAAC,EAAAZ,EAAA,OAAAa,mBAAAC,KAAA,SAAAC,GAAA,cAAAA,EAAAC,KAAAD,EAAAE,MAAA,cAAAF,EAAAC,KAAA,EAAAD,EAAAE,KAAA,EAE0BC,QAAQC,IAAI,CAChCC,YAAqBjC,KAAKkC,KAAMX,GAAhC,MAAiD,WAC/C,MAAM,IAAIY,MAAM,iCAElBC,YAAoBpC,KAAKkC,MAAzB,MAAqC,WACnC,MAAM,IAAIC,MAAM,2CAPxB,UAAAX,EAAAI,EAAAS,KAAAZ,EAAAa,EAAAd,EAAA,IAEWX,EAFXY,EAAA,IAWec,QAXf,CAAAX,EAAAE,KAAA,cAYY,IAAIK,MAAM,wCAZtB,OAeInC,KAAKC,OAASY,EAflBe,EAAAE,KAAA,iBAAAF,EAAAC,KAAA,GAAAD,EAAAY,GAAAZ,EAAA,SAkBIa,QAAQC,MAARd,EAAAY,IACAG,MAAMf,EAAAY,GAAII,SAAW,mCACrBC,QAAQC,OApBZ,yBAAAlB,EAAAmB,SAAAzB,EAAAtB,KAAA,yRAwBA,WACE,OAAOgD,YAAPC,UA7D4BC","file":"chunk.35929da61d769e57c884.js","sourcesContent":["import {\n LitElement,\n customElement,\n property,\n TemplateResult,\n html,\n PropertyValues,\n CSSResult,\n css,\n} from \"lit-element\";\nimport { HomeAssistant, Route } from \"../../../src/types\";\nimport { createHassioSession } from \"../../../src/data/hassio/supervisor\";\nimport {\n HassioAddonDetails,\n fetchHassioAddonInfo,\n} from \"../../../src/data/hassio/addon\";\nimport \"../../../src/layouts/hass-loading-screen\";\nimport \"../../../src/layouts/hass-subpage\";\n\n@customElement(\"hassio-ingress-view\")\nclass HassioIngressView extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public route!: Route;\n @property() private _addon?: HassioAddonDetails;\n\n protected render(): TemplateResult {\n if (!this._addon) {\n return html`\n <hass-loading-screen></hass-loading-screen>\n `;\n }\n\n return html`\n <hass-subpage .header=${this._addon.name} hassio>\n <iframe src=${this._addon.ingress_url}></iframe>\n </hass-subpage>\n `;\n }\n\n protected updated(changedProps: PropertyValues) {\n super.firstUpdated(changedProps);\n\n if (!changedProps.has(\"route\")) {\n return;\n }\n\n const addon = this.route.path.substr(1);\n\n const oldRoute = changedProps.get(\"route\") as this[\"route\"] | undefined;\n const oldAddon = oldRoute ? oldRoute.path.substr(1) : undefined;\n\n if (addon && addon !== oldAddon) {\n this._fetchData(addon);\n }\n }\n\n private async _fetchData(addonSlug: string) {\n try {\n const [addon] = await Promise.all([\n fetchHassioAddonInfo(this.hass, addonSlug).catch(() => {\n throw new Error(\"Failed to fetch add-on info\");\n }),\n createHassioSession(this.hass).catch(() => {\n throw new Error(\"Failed to create an ingress session\");\n }),\n ]);\n\n if (!addon.ingress) {\n throw new Error(\"This add-on does not support ingress\");\n }\n\n this._addon = addon;\n } catch (err) {\n // tslint:disable-next-line\n console.error(err);\n alert(err.message || \"Unknown error starting ingress.\");\n history.back();\n }\n }\n\n static get styles(): CSSResult {\n return css`\n iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n }\n paper-icon-button {\n color: var(--text-primary-color);\n }\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-ingress-view\": HassioIngressView;\n }\n}\n"],"sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,189 @@
/**
* @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) 2019 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 2018 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
@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) 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
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/**
* @license
* Copyright (c) 2018 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 2019 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @license
* Copyright 2016 Google Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
@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) 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
*/
/*!
* Fuse.js v3.4.4 - Lightweight fuzzy-search (http://fusejs.io)
*
* Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me)
* All Rights Reserved. Apache Software License 2.0
*
* http://www.apache.org/licenses/LICENSE-2.0
*/

Binary file not shown.

File diff suppressed because one or more lines are too long

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

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

Some files were not shown because too many files have changed in this diff Show More