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 \ containerd.io \
&& rm -rf /var/lib/apt/lists/* && 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 # Install Python dependencies from requirements.txt if it exists
COPY requirements.txt requirements_tests.txt ./ COPY requirements.txt requirements_tests.txt ./
RUN pip3 install -r requirements.txt -r 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. // See https://aka.ms/vscode-remote/devcontainer.json for format details.
{ {
"name": "Hass.io dev", "name": "Supervisor dev",
"context": "..", "context": "..",
"dockerFile": "Dockerfile", "dockerFile": "Dockerfile",
"appPort": "9123:8123", "appPort": "9123:8123",
"runArgs": [ "runArgs": ["-e", "GIT_EDITOR=code --wait", "--privileged"],
"-e", "extensions": [
"GIT_EDITOR=code --wait", "ms-python.python",
"--privileged" "visualstudioexptteam.vscodeintellicode",
], "esbenp.prettier-vscode"
"extensions": [ ],
"ms-python.python", "settings": {
"visualstudioexptteam.vscodeintellicode", "python.pythonPath": "/usr/local/bin/python",
"esbenp.prettier-vscode" "python.linting.pylintEnabled": true,
], "python.linting.enabled": true,
"settings": { "python.formatting.provider": "black",
"python.pythonPath": "/usr/local/bin/python", "python.formatting.blackArgs": ["--target-version", "py37"],
"python.linting.pylintEnabled": true, "editor.formatOnPaste": false,
"python.linting.enabled": true, "editor.formatOnSave": true,
"python.formatting.provider": "black", "editor.formatOnType": true,
"python.formatting.blackArgs": [ "files.trimTrailingWhitespace": true
"--target-version", }
"py37" }
],
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true
}
}

View File

@@ -14,10 +14,10 @@
# virtualenv # virtualenv
venv/ venv/
# HA # Data
home-assistant-polymer/* home-assistant-polymer/
misc/* script/
script/* tests/
# Test ENV # Test ENV
data/ 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 ### Auth / SSO API
You can use the user system on homeassistant. We handle this auth system on 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 # Install base
RUN apk add --no-cache \ RUN apk add --no-cache \
openssl \
libffi \
musl \
git \
socat \
glib \
eudev \ eudev \
eudev-libs eudev-libs \
git \
glib \
libffi \
libpulse \
musl \
openssl \
socat
ARG BUILD_ARCH ARG BUILD_ARCH
WORKDIR /usr/src WORKDIR /usr/src
@@ -23,15 +24,11 @@ RUN export MAKEFLAGS="-j$(nproc)" \
-r ./requirements.txt \ -r ./requirements.txt \
&& rm -f requirements.txt && rm -f requirements.txt
# Install HassIO # Install Home Assistant Supervisor
COPY . hassio COPY . supervisor
RUN pip3 install --no-cache-dir -e ./hassio \ RUN pip3 install --no-cache-dir -e ./supervisor \
&& python3 -m compileall ./hassio/hassio && python3 -m compileall ./supervisor/supervisor
# Initialize udev daemon, handle CMD
COPY entry.sh /bin/
ENTRYPOINT ["/bin/entry.sh"]
WORKDIR / WORKDIR /
CMD [ "python3", "-m", "hassio" ] COPY rootfs /

View File

@@ -1,3 +1,3 @@
include LICENSE.md include LICENSE.md
graft hassio graft supervisor
recursive-exclude * *.py[co] 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) [![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 ## 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 installation. This includes changing network settings or installing
and updating software. and updating software.
![](misc/hassio.png?raw=true)
## Installation ## Installation
Installation instructions can be found at <https://home-assistant.io/hassio>. Installation instructions can be found at <https://home-assistant.io/hassio>.

View File

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

View File

@@ -10,10 +10,8 @@ trigger:
- "*" - "*"
pr: none pr: none
variables: variables:
- name: basePythonTag
value: "3.7-alpine3.11"
- name: versionBuilder - name: versionBuilder
value: "6.9" value: "7.0"
- group: docker - group: docker
jobs: jobs:
@@ -51,6 +49,5 @@ jobs:
-v ~/.docker:/root/.docker \ -v ~/.docker:/root/.docker \
-v /run/docker.sock:/run/docker.sock:rw -v $(pwd):/data:ro \ -v /run/docker.sock:/run/docker.sock:rw -v $(pwd):/data:ro \
homeassistant/amd64-builder:$(versionBuilder) \ homeassistant/amd64-builder:$(versionBuilder) \
--supervisor $(basePythonTag) --version $(Build.SourceBranchName) \ --generic $(Build.SourceBranchName) --all -t /data
--all -t /data --docker-hub homeassistant
displayName: "Build Release" 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 cpe==1.2.1
cryptography==2.8 cryptography==2.8
docker==4.2.0 docker==4.2.0
gitpython==3.0.7 gitpython==3.1.0
jinja2==2.11.1
packaging==20.1 packaging==20.1
ptvsd==4.3.2
pulsectl==20.2.4
pytz==2019.3 pytz==2019.3
pyudev==0.22.0 pyudev==0.22.0
ruamel.yaml==0.15.100 ruamel.yaml==0.15.100
uvloop==0.14.0 uvloop==0.14.0
voluptuous==0.11.7 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 \ docker run --rm --privileged \
-v /run/docker.sock:/run/docker.sock -v "$(pwd):/data" \ -v /run/docker.sock:/run/docker.sock -v "$(pwd):/data" \
homeassistant/amd64-builder:dev \ homeassistant/amd64-builder:dev \
--supervisor 3.7-alpine3.11 --version dev \ --generic dev -t /data --test --amd64 --no-cache
-t /data --test --amd64 \
--no-cache --docker-hub homeassistant
} }
@@ -79,7 +77,7 @@ function cleanup_lastboot() {
function cleanup_docker() { function cleanup_docker() {
echo "Cleaning up stopped containers..." 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" echo "Start Test-Env"
start_docker start_docker
@@ -117,5 +134,6 @@ build_supervisor
install_cli install_cli
cleanup_lastboot cleanup_lastboot
cleanup_docker cleanup_docker
init_dbus
setup_test_env setup_test_env
stop_docker stop_docker

View File

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

View File

@@ -1,10 +1,11 @@
"""Home Assistant Supervisor setup."""
from setuptools import setup from setuptools import setup
from hassio.const import HASSIO_VERSION from supervisor.const import SUPERVISOR_VERSION
setup( setup(
name="HassIO", name="Supervisor",
version=HASSIO_VERSION, version=SUPERVISOR_VERSION,
license="BSD License", license="BSD License",
author="The Home Assistant Authors", author="The Home Assistant Authors",
author_email="hello@home-assistant.io", author_email="hello@home-assistant.io",
@@ -24,19 +25,19 @@ setup(
"Topic :: Scientific/Engineering :: Atmospheric Science", "Topic :: Scientific/Engineering :: Atmospheric Science",
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7",
], ],
keywords=["docker", "home-assistant", "api"], keywords=["docker", "home-assistant", "api"],
zip_safe=False, zip_safe=False,
platforms="any", platforms="any",
packages=[ packages=[
"hassio", "supervisor",
"hassio.docker", "supervisor.docker",
"hassio.addons", "supervisor.addons",
"hassio.api", "supervisor.api",
"hassio.misc", "supervisor.misc",
"hassio.utils", "supervisor.utils",
"hassio.snapshots", "supervisor.snapshots",
], ],
include_package_data=True, 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 import asyncio
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
import logging import logging
import sys import sys
from hassio import bootstrap from supervisor import bootstrap
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -29,7 +29,7 @@ if __name__ == "__main__":
# Init async event loop # Init async event loop
loop = initialize_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(): if not bootstrap.check_environment():
sys.exit(1) sys.exit(1)
@@ -37,27 +37,27 @@ if __name__ == "__main__":
executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker") executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker")
loop.set_default_executor(executor) loop.set_default_executor(executor)
_LOGGER.info("Initialize Hass.io setup") _LOGGER.info("Initialize Supervisor setup")
coresys = loop.run_until_complete(bootstrap.initialize_coresys()) coresys = loop.run_until_complete(bootstrap.initialize_coresys())
loop.run_until_complete(coresys.core.connect()) loop.run_until_complete(coresys.core.connect())
bootstrap.supervisor_debugger(coresys) bootstrap.supervisor_debugger(coresys)
bootstrap.migrate_system_env(coresys) bootstrap.migrate_system_env(coresys)
_LOGGER.info("Setup HassIO") _LOGGER.info("Setup Supervisor")
loop.run_until_complete(coresys.core.setup()) loop.run_until_complete(coresys.core.setup())
loop.call_soon_threadsafe(loop.create_task, coresys.core.start()) loop.call_soon_threadsafe(loop.create_task, coresys.core.start())
loop.call_soon_threadsafe(bootstrap.reg_signal, loop) loop.call_soon_threadsafe(bootstrap.reg_signal, loop)
try: try:
_LOGGER.info("Run Hass.io") _LOGGER.info("Run Supervisor")
loop.run_forever() loop.run_forever()
finally: finally:
_LOGGER.info("Stopping Hass.io") _LOGGER.info("Stopping Supervisor")
loop.run_until_complete(coresys.core.stop()) loop.run_until_complete(coresys.core.stop())
executor.shutdown(wait=False) executor.shutdown(wait=False)
loop.close() loop.close()
_LOGGER.info("Close Hass.io") _LOGGER.info("Close Supervisor")
sys.exit(0) sys.exit(0)

View File

@@ -1,4 +1,4 @@
"""Init file for Hass.io add-ons.""" """Init file for Supervisor add-ons."""
import asyncio import asyncio
from contextlib import suppress from contextlib import suppress
import logging import logging
@@ -25,7 +25,7 @@ AnyAddon = Union[Addon, AddonStore]
class AddonManager(CoreSysAttributes): class AddonManager(CoreSysAttributes):
"""Manage add-ons inside Hass.io.""" """Manage add-ons inside Supervisor."""
def __init__(self, coresys: CoreSys): def __init__(self, coresys: CoreSys):
"""Initialize Docker base wrapper.""" """Initialize Docker base wrapper."""
@@ -57,7 +57,7 @@ class AddonManager(CoreSysAttributes):
return self.store.get(addon_slug) return self.store.get(addon_slug)
def from_token(self, token: str) -> Optional[Addon]: 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: for addon in self.installed:
if token == addon.hassio_token: if token == addon.hassio_token:
return addon return addon
@@ -152,9 +152,9 @@ class AddonManager(CoreSysAttributes):
await addon.remove_data() await addon.remove_data()
# Cleanup audio settings # Cleanup audio settings
if addon.path_asound.exists(): if addon.path_pulse.exists():
with suppress(OSError): with suppress(OSError):
addon.path_asound.unlink() addon.path_pulse.unlink()
# Cleanup AppArmor profile # Cleanup AppArmor profile
with suppress(HostAppArmorError): 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 contextlib import suppress
from copy import deepcopy from copy import deepcopy
from ipaddress import IPv4Address from ipaddress import IPv4Address
@@ -63,9 +63,11 @@ RE_WEBUI = re.compile(
r":\/\/\[HOST\]:\[PORT:(?P<t_port>\d+)\](?P<s_suffix>.*)$" r":\/\/\[HOST\]:\[PORT:(?P<t_port>\d+)\](?P<s_suffix>.*)$"
) )
RE_OLD_AUDIO = re.compile(r"\d+,\d+")
class Addon(AddonModel): 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): def __init__(self, coresys: CoreSys, slug: str):
"""Initialize data holder.""" """Initialize data holder."""
@@ -163,12 +165,12 @@ class Addon(AddonModel):
@property @property
def hassio_token(self) -> Optional[str]: 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) return self.persist.get(ATTR_ACCESS_TOKEN)
@property @property
def ingress_token(self) -> Optional[str]: 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) return self.persist.get(ATTR_INGRESS_TOKEN)
@property @property
@@ -250,7 +252,7 @@ class Addon(AddonModel):
# lookup the correct protocol from config # lookup the correct protocol from config
if t_proto: if t_proto:
proto = "https" if self.options[t_proto] else "http" proto = "https" if self.options.get(t_proto) else "http"
else: else:
proto = s_prefix proto = s_prefix
@@ -279,14 +281,20 @@ class Addon(AddonModel):
@property @property
def audio_output(self) -> Optional[str]: 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: if not self.with_audio:
return None 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 @audio_output.setter
def audio_output(self, value: Optional[str]): def audio_output(self, value: Optional[str]):
"""Set/reset audio output settings.""" """Set/reset audio output profile settings."""
if value is None: if value is None:
self.persist.pop(ATTR_AUDIO_OUTPUT, None) self.persist.pop(ATTR_AUDIO_OUTPUT, None)
else: else:
@@ -294,10 +302,16 @@ class Addon(AddonModel):
@property @property
def audio_input(self) -> Optional[str]: 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: if not self.with_audio:
return None 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 @audio_input.setter
def audio_input(self, value: Optional[str]): def audio_input(self, value: Optional[str]):
@@ -333,14 +347,14 @@ class Addon(AddonModel):
return Path(self.path_data, "options.json") return Path(self.path_data, "options.json")
@property @property
def path_asound(self): def path_pulse(self):
"""Return path to asound config.""" """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 @property
def path_extern_asound(self): def path_extern_pulse(self):
"""Return path to asound config for Docker.""" """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): def save_persist(self):
"""Save data of add-on.""" """Save data of add-on."""
@@ -379,20 +393,24 @@ class Addon(AddonModel):
_LOGGER.info("Remove add-on data folder %s", self.path_data) _LOGGER.info("Remove add-on data folder %s", self.path_data)
await remove_data(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.""" """Write asound config to file and return True on success."""
asound_config = self.sys_host.alsa.asound( pulse_config = self.sys_audio.pulse_client(
alsa_input=self.audio_input, alsa_output=self.audio_output input_profile=self.audio_input, output_profile=self.audio_output
) )
try: try:
with self.path_asound.open("w") as config_file: with self.path_pulse.open("w") as config_file:
config_file.write(asound_config) config_file.write(pulse_config)
except OSError as err: 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() 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: async def install_apparmor(self) -> None:
"""Install or Update AppArmor profile for Add-on.""" """Install or Update AppArmor profile for Add-on."""
@@ -468,7 +486,7 @@ class Addon(AddonModel):
# Sound # Sound
if self.with_audio: if self.with_audio:
self.write_asound() self.write_pulse()
# Start Add-on # Start Add-on
try: try:

View File

@@ -1,4 +1,4 @@
"""Hass.io add-on build environment.""" """Supervisor add-on build environment."""
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Dict from typing import TYPE_CHECKING, Dict
@@ -16,7 +16,7 @@ class AddonBuild(JsonConfig, CoreSysAttributes):
"""Handle build options for add-ons.""" """Handle build options for add-ons."""
def __init__(self, coresys: CoreSys, addon: AnyAddon) -> None: def __init__(self, coresys: CoreSys, addon: AnyAddon) -> None:
"""Initialize Hass.io add-on builder.""" """Initialize Supervisor add-on builder."""
self.coresys: CoreSys = coresys self.coresys: CoreSys = coresys
self.addon = addon 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 from copy import deepcopy
import logging import logging
from typing import Any, Dict from typing import Any, Dict
@@ -23,7 +23,7 @@ Config = Dict[str, Any]
class AddonsData(JsonConfig, CoreSysAttributes): 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): def __init__(self, coresys: CoreSys):
"""Initialize data holder.""" """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 pathlib import Path
from typing import Any, Awaitable, Dict, List, Optional from typing import Any, Awaitable, Dict, List, Optional
@@ -31,6 +31,7 @@ from ..const import (
ATTR_HOST_PID, ATTR_HOST_PID,
ATTR_IMAGE, ATTR_IMAGE,
ATTR_INGRESS, ATTR_INGRESS,
ATTR_INIT,
ATTR_KERNEL_MODULES, ATTR_KERNEL_MODULES,
ATTR_LEGACY, ATTR_LEGACY,
ATTR_LOCATON, ATTR_LOCATON,
@@ -137,12 +138,12 @@ class AddonModel(CoreSysAttributes):
@property @property
def hassio_token(self) -> Optional[str]: def hassio_token(self) -> Optional[str]:
"""Return access token for Hass.io API.""" """Return access token for Supervisor API."""
return None return None
@property @property
def ingress_token(self) -> Optional[str]: def ingress_token(self) -> Optional[str]:
"""Return access token for Hass.io API.""" """Return access token for Supervisor API."""
return None return None
@property @property
@@ -326,7 +327,7 @@ class AddonModel(CoreSysAttributes):
@property @property
def access_hassio_api(self) -> bool: 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] return self.data[ATTR_HASSIO_API]
@property @property
@@ -336,7 +337,7 @@ class AddonModel(CoreSysAttributes):
@property @property
def hassio_role(self) -> str: def hassio_role(self) -> str:
"""Return Hass.io role for API.""" """Return Supervisor role for API."""
return self.data[ATTR_HASSIO_ROLE] return self.data[ATTR_HASSIO_ROLE]
@property @property
@@ -344,6 +345,11 @@ class AddonModel(CoreSysAttributes):
"""Return Exclude list for snapshot.""" """Return Exclude list for snapshot."""
return self.data.get(ATTR_SNAPSHOT_EXCLUDE, []) 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 @property
def with_stdin(self) -> bool: def with_stdin(self) -> bool:
"""Return True if the add-on access use stdin input.""" """Return True if the add-on access use stdin input."""

View File

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

View File

@@ -44,6 +44,7 @@ from ..const import (
ATTR_INGRESS_PANEL, ATTR_INGRESS_PANEL,
ATTR_INGRESS_PORT, ATTR_INGRESS_PORT,
ATTR_INGRESS_TOKEN, ATTR_INGRESS_TOKEN,
ATTR_INIT,
ATTR_KERNEL_MODULES, ATTR_KERNEL_MODULES,
ATTR_LEGACY, ATTR_LEGACY,
ATTR_LOCATON, ATTR_LOCATON,
@@ -96,7 +97,6 @@ from ..discovery.validate import valid_discovery_service
from ..validate import ( from ..validate import (
DOCKER_PORTS, DOCKER_PORTS,
DOCKER_PORTS_DESCRIPTION, DOCKER_PORTS_DESCRIPTION,
alsa_device,
network_port, network_port,
token, token,
uuid_match, uuid_match,
@@ -190,6 +190,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Optional(ATTR_URL): vol.Url(), vol.Optional(ATTR_URL): vol.Url(),
vol.Required(ATTR_STARTUP): vol.All(_simple_startup, vol.In(STARTUP_ALL)), vol.Required(ATTR_STARTUP): vol.All(_simple_startup, vol.In(STARTUP_ALL)),
vol.Required(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]), 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_ADVANCED, default=False): vol.Boolean(),
vol.Optional(ATTR_STAGE, default=AddonStages.STABLE): vol.Coerce(AddonStages), vol.Optional(ATTR_STAGE, default=AddonStages.STABLE): vol.Coerce(AddonStages),
vol.Optional(ATTR_PORTS): DOCKER_PORTS, 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_AUTO_UPDATE, default=False): vol.Boolean(),
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]), vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_NETWORK): DOCKER_PORTS, vol.Optional(ATTR_NETWORK): DOCKER_PORTS,
vol.Optional(ATTR_AUDIO_OUTPUT): alsa_device, vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_AUDIO_INPUT): alsa_device, vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(), vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(),
vol.Optional(ATTR_INGRESS_PANEL, default=False): 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 import logging
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
@@ -21,6 +21,7 @@ from .security import SecurityMiddleware
from .services import APIServices from .services import APIServices
from .snapshots import APISnapshots from .snapshots import APISnapshots
from .supervisor import APISupervisor from .supervisor import APISupervisor
from .audio import APIAudio
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -29,7 +30,7 @@ MAX_CLIENT_SIZE: int = 1024 ** 2 * 16
class RestAPI(CoreSysAttributes): class RestAPI(CoreSysAttributes):
"""Handle RESTful API for Hass.io.""" """Handle RESTful API for Supervisor."""
def __init__(self, coresys: CoreSys): def __init__(self, coresys: CoreSys):
"""Initialize Docker base wrapper.""" """Initialize Docker base wrapper."""
@@ -61,6 +62,7 @@ class RestAPI(CoreSysAttributes):
self._register_info() self._register_info()
self._register_auth() self._register_auth()
self._register_dns() self._register_dns()
self._register_audio()
def _register_host(self) -> None: def _register_host(self) -> None:
"""Register hostcontrol functions.""" """Register hostcontrol functions."""
@@ -93,7 +95,7 @@ class RestAPI(CoreSysAttributes):
web.post("/os/update", api_hassos.update), web.post("/os/update", api_hassos.update),
web.post("/os/update/cli", api_hassos.update_cli), web.post("/os/update/cli", api_hassos.update_cli),
web.post("/os/config/sync", api_hassos.config_sync), 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.get("/hassos/info", api_hassos.info),
web.post("/hassos/update", api_hassos.update), web.post("/hassos/update", api_hassos.update),
web.post("/hassos/update/cli", api_hassos.update_cli), 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/start", api_hass.start),
web.post("/core/check", api_hass.check), web.post("/core/check", api_hass.check),
web.post("/core/rebuild", api_hass.rebuild), 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/info", api_hass.info),
web.get("/homeassistant/logs", api_hass.logs), web.get("/homeassistant/logs", api_hass.logs),
web.get("/homeassistant/stats", api_hass.stats), web.get("/homeassistant/stats", api_hass.stats),
@@ -192,7 +194,7 @@ class RestAPI(CoreSysAttributes):
web.post("/core/api/{path:.+}", api_proxy.api), web.post("/core/api/{path:.+}", api_proxy.api),
web.get("/core/api/{path:.+}", api_proxy.api), web.get("/core/api/{path:.+}", api_proxy.api),
web.get("/core/api/", 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/api/websocket", api_proxy.websocket),
web.get("/homeassistant/websocket", api_proxy.websocket), web.get("/homeassistant/websocket", api_proxy.websocket),
web.get("/homeassistant/api/stream", api_proxy.stream), 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: def _register_panel(self) -> None:
"""Register panel for Home Assistant.""" """Register panel for Home Assistant."""
panel_dir = Path(__file__).parent.joinpath("panel") 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 asyncio
import logging import logging
from typing import Any, Awaitable, Dict, List from typing import Any, Awaitable, Dict, List
@@ -96,7 +96,7 @@ from ..const import (
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..docker.stats import DockerStats from ..docker.stats import DockerStats
from ..exceptions import APIError 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 from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _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( SCHEMA_OPTIONS = vol.Schema(
{ {
vol.Optional(ATTR_BOOT): vol.In([BOOT_AUTO, BOOT_MANUAL]), 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_AUTO_UPDATE): vol.Boolean(),
vol.Optional(ATTR_AUDIO_OUTPUT): alsa_device, vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_AUDIO_INPUT): alsa_device, vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
vol.Optional(ATTR_INGRESS_PANEL): vol.Boolean(), 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 asyncio
import logging import logging
from typing import Dict from typing import Dict
@@ -76,7 +76,7 @@ class APIAuth(CoreSysAttributes):
return await self._process_dict(request, addon, data) return await self._process_dict(request, addon, data)
raise HTTPUnauthorized( raise HTTPUnauthorized(
headers={WWW_AUTHENTICATE: 'Basic realm="Hass.io Authentication"'} headers={WWW_AUTHENTICATE: 'Basic realm="Home Assistant Authentication"'}
) )
@api_process @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 import voluptuous as vol
from .utils import api_process, api_validate 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 asyncio
import logging import logging
from typing import Any, Awaitable, Dict 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 asyncio
import logging import logging
from typing import Any, Dict from typing import Any, Dict
@@ -37,11 +37,17 @@ class APIHardware(CoreSysAttributes):
@api_process @api_process
async def audio(self, request: web.Request) -> Dict[str, Any]: async def audio(self, request: web.Request) -> Dict[str, Any]:
"""Show ALSA audio devices.""" """Show pulse audio profiles."""
return { return {
ATTR_AUDIO: { ATTR_AUDIO: {
ATTR_INPUT: self.sys_host.alsa.input_devices, ATTR_INPUT: {
ATTR_OUTPUT: self.sys_host.alsa.output_devices, 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 asyncio
import logging import logging
from typing import Any, Awaitable, Dict 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 asyncio
import logging import logging
from typing import Coroutine, Dict, Any 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 asyncio
import logging 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 import logging
from typing import Any, Dict from typing import Any, Dict

View File

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