diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b70e4b43c..5283c2587 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -6,6 +6,8 @@ repos:
args:
- --safe
- --quiet
+ - --target-version
+ - py38
files: ^((supervisor|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3
diff --git a/API.md b/API.md
index 12bbb3df1..276f5f007 100644
--- a/API.md
+++ b/API.md
@@ -466,7 +466,7 @@ Get network information
}
```
-#### GET `/network/{interface}/info`
+#### GET `/network/interface/{interface}/info`
Get information for a single interface
@@ -482,7 +482,7 @@ Get information for a single interface
}
```
-#### POST `/network/{interface}/update`
+#### POST `/network/interface/{interface}/update`
Update information for a single interface
@@ -491,7 +491,7 @@ Update information for a single interface
| Option | Description |
| --------- | ---------------------------------------------------------------------- |
| `address` | The new IP address for the interface in the X.X.X.X/XX format |
-| `dns` | Comma seperated list of DNS servers to use |
+| `dns` | List of DNS servers to use |
| `gateway` | The gateway the interface should use |
| `method` | Set if the interface should use DHCP or not, can be `dhcp` or `static` |
@@ -565,6 +565,7 @@ Get all available add-ons.
"version": "null|VERSION_INSTALLED",
"version_latest": "version_latest",
"state": "none|started|stopped",
+ "startup": "initialize|system|services|application|once",
"boot": "auto|manual",
"build": "bool",
"options": "{}",
@@ -619,6 +620,7 @@ Get all available add-ons.
- GET `/addons/{addon}/changelog`
- GET `/addons/{addon}/documentation`
- POST `/addons/{addon}/options`
+- POST `/addons/{addon}/options/validate`
```json
{
diff --git a/codecov.yaml b/codecov.yaml
index e8f6de918..068110729 100644
--- a/codecov.yaml
+++ b/codecov.yaml
@@ -7,3 +7,5 @@ coverage:
target: 40
threshold: 0.09
comment: false
+github_checks:
+ annotations: false
\ No newline at end of file
diff --git a/home-assistant-polymer b/home-assistant-polymer
index dc5b92030..faee2c3e1 160000
--- a/home-assistant-polymer
+++ b/home-assistant-polymer
@@ -1 +1 @@
-Subproject commit dc5b92030f2182221a25e591df0dc8760a836bb6
+Subproject commit faee2c3e1b16aa758a802b43d39bcb7032bcf722
diff --git a/requirements.txt b/requirements.txt
index 1cd2a3b95..51509f810 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,13 +7,13 @@ cpe==1.2.1
cryptography==3.1
debugpy==1.0.0rc2
docker==4.3.1
-gitpython==3.1.7
+gitpython==3.1.8
jinja2==2.11.2
packaging==20.4
pulsectl==20.5.1
pytz==2020.1
pyudev==0.22.0
ruamel.yaml==0.15.100
-sentry-sdk==0.17.0
+sentry-sdk==0.17.3
uvloop==0.14.0
voluptuous==0.11.7
diff --git a/requirements_tests.txt b/requirements_tests.txt
index b3dbf4efb..6c24f2e32 100644
--- a/requirements_tests.txt
+++ b/requirements_tests.txt
@@ -4,7 +4,7 @@ coverage==5.2.1
flake8-docstrings==1.5.0
flake8==3.8.3
pre-commit==2.7.1
-pydocstyle==5.1.0
+pydocstyle==5.1.1
pylint==2.6.0
pytest-aiohttp==0.3.0
pytest-asyncio==0.12.0 # NB!: Versions over 0.12.0 breaks pytest-aiohttp (https://github.com/aio-libs/pytest-aiohttp/issues/16)
diff --git a/supervisor/addons/addon.py b/supervisor/addons/addon.py
index fae0bc346..c0a520d07 100644
--- a/supervisor/addons/addon.py
+++ b/supervisor/addons/addon.py
@@ -39,6 +39,7 @@ from ..const import (
ATTR_VERSION,
ATTR_WATCHDOG,
DNS_SUFFIX,
+ AddonStartup,
AddonState,
)
from ..coresys import CoreSys
@@ -185,7 +186,12 @@ class Addon(AddonModel):
@watchdog.setter
def watchdog(self, value: bool) -> None:
"""Set watchdog enable/disable."""
- self.persist[ATTR_WATCHDOG] = value
+ if value and self.startup == AddonStartup.ONCE:
+ _LOGGER.warning(
+ "Ignoring watchdog for %s because startup type is 'once'", self.slug
+ )
+ else:
+ self.persist[ATTR_WATCHDOG] = value
@property
def uuid(self) -> str:
@@ -552,7 +558,7 @@ class Addon(AddonModel):
await self.instance.run()
except DockerAPIError as err:
self.state = AddonState.ERROR
- raise AddonsError() from err
+ raise AddonsError(err) from None
else:
self.state = AddonState.STARTED
diff --git a/supervisor/api/__init__.py b/supervisor/api/__init__.py
index d711384fd..7dcb548ba 100644
--- a/supervisor/api/__init__.py
+++ b/supervisor/api/__init__.py
@@ -268,6 +268,9 @@ class RestAPI(CoreSysAttributes):
web.post("/addons/{addon}/restart", api_addons.restart),
web.post("/addons/{addon}/update", api_addons.update),
web.post("/addons/{addon}/options", api_addons.options),
+ web.post(
+ "/addons/{addon}/options/validate", api_addons.options_validate
+ ),
web.post("/addons/{addon}/rebuild", api_addons.rebuild),
web.get("/addons/{addon}/logs", api_addons.logs),
web.get("/addons/{addon}/icon", api_addons.icon),
diff --git a/supervisor/api/addons.py b/supervisor/api/addons.py
index c01e442ac..0ddfcbcff 100644
--- a/supervisor/api/addons.py
+++ b/supervisor/api/addons.py
@@ -5,6 +5,7 @@ from typing import Any, Awaitable, Dict, List
from aiohttp import web
import voluptuous as vol
+from voluptuous.humanize import humanize_error
from ..addons import AnyAddon
from ..addons.addon import Addon
@@ -61,6 +62,7 @@ from ..const import (
ATTR_MEMORY_LIMIT,
ATTR_MEMORY_PERCENT,
ATTR_MEMORY_USAGE,
+ ATTR_MESSAGE,
ATTR_NAME,
ATTR_NETWORK,
ATTR_NETWORK_DESCRIPTION,
@@ -77,11 +79,13 @@ from ..const import (
ATTR_SLUG,
ATTR_SOURCE,
ATTR_STAGE,
+ ATTR_STARTUP,
ATTR_STATE,
ATTR_STDIN,
ATTR_UDEV,
ATTR_URL,
ATTR_USB,
+ ATTR_VALID,
ATTR_VERSION,
ATTR_VERSION_LATEST,
ATTR_VIDEO,
@@ -249,6 +253,7 @@ class APIAddons(CoreSysAttributes):
ATTR_AUDIO: addon.with_audio,
ATTR_AUDIO_INPUT: None,
ATTR_AUDIO_OUTPUT: None,
+ ATTR_STARTUP: addon.startup,
ATTR_SERVICES: _pretty_services(addon),
ATTR_DISCOVERY: addon.discovery,
ATTR_IP_ADDRESS: None,
@@ -315,6 +320,19 @@ class APIAddons(CoreSysAttributes):
addon.save_persist()
+ @api_process
+ async def options_validate(self, request: web.Request) -> None:
+ """Validate user options for add-on."""
+ addon = self._extract_addon_installed(request)
+ data = {ATTR_MESSAGE: "", ATTR_VALID: True}
+ try:
+ addon.schema(addon.options)
+ except vol.Invalid as ex:
+ data[ATTR_MESSAGE] = humanize_error(addon.options, ex)
+ data[ATTR_VALID] = False
+
+ return data
+
@api_process
async def security(self, request: web.Request) -> None:
"""Store security options for add-on."""
diff --git a/supervisor/api/panel/entrypoint.js b/supervisor/api/panel/entrypoint.js
index abba8a49f..98811605a 100644
--- a/supervisor/api/panel/entrypoint.js
+++ b/supervisor/api/panel/entrypoint.js
@@ -1,9 +1,9 @@
try {
- new Function("import('/api/hassio/app/frontend_latest/entrypoint.18a06d96.js')")();
+ new Function("import('/api/hassio/app/frontend_latest/entrypoint.e3f59ee9.js')")();
} catch (err) {
var el = document.createElement('script');
- el.src = '/api/hassio/app/frontend_es5/entrypoint.10e8b77d.js';
+ el.src = '/api/hassio/app/frontend_es5/entrypoint.6086a806.js';
document.body.appendChild(el);
}
\ No newline at end of file
diff --git a/supervisor/api/panel/entrypoint.js.gz b/supervisor/api/panel/entrypoint.js.gz
index 43e1af929..3ce54e070 100644
Binary files a/supervisor/api/panel/entrypoint.js.gz and b/supervisor/api/panel/entrypoint.js.gz differ
diff --git a/supervisor/api/panel/frontend_es5/chunk.0a106e488ed654ffce49.js b/supervisor/api/panel/frontend_es5/chunk.0a106e488ed654ffce49.js
new file mode 100644
index 000000000..5883b72cd
--- /dev/null
+++ b/supervisor/api/panel/frontend_es5/chunk.0a106e488ed654ffce49.js
@@ -0,0 +1,2 @@
+(self.webpackJsonp=self.webpackJsonp||[]).push([[2],{161:function(e,t,r){"use strict";r.r(t);r(44),r(81);var n=r(0),i=r(14),o=(r(102),r(103),r(13)),a=r(11);function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function c(){var e=h(["\n :host([inert]) {\n pointer-events: initial !important;\n cursor: initial !important;\n }\n a {\n color: var(--primary-color);\n }\n p {\n margin: 0;\n padding-top: 6px;\n padding-bottom: 24px;\n color: var(--primary-text-color);\n }\n .no-bottom-padding {\n padding-bottom: 0;\n }\n .secondary {\n color: var(--secondary-text-color);\n }\n ha-dialog {\n /* Place above other dialogs */\n --dialog-z-index: 104;\n }\n "]);return c=function(){return e},e}function l(){var e=h(["\n
\n ","\n
\n "]);return p=function(){return e},e}function f(){var e=h(["\nError: ',"
"]);return h=function(){return e},e}function f(){var e=w(['\nError: ',"
"]);return p=function(){return e},e}function h(){var e=b(['\n\n ","\n
\n "]);return p=function(){return e},e}function f(){var e=h(["\n