Initial WS support (#2439)

* Initial WS support

* test

* Update frontend to fc7c4af2

* Fix issue with closing states

* log error

* make data optional

* limit stopping states

* Move wrappers to HomeAssistantWebSocket

* use info

* Use call_soon

* Use lookuptable for WS commands

* Fix tests
This commit is contained in:
Joakim Sørensen 2021-02-19 11:57:31 +01:00 committed by GitHub
parent c342231052
commit b31ecfefcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 463 additions and 220 deletions

@ -1 +1 @@
Subproject commit 03d4174163166fa420b0cc7077ef2fa469f1164d
Subproject commit fc7c4af27ac0d8ccfa0d23b2405b7523adcb0f92

View File

@ -1,9 +1,9 @@
try {
new Function("import('/api/hassio/app/frontend_latest/entrypoint.5c3eb78c.js')")();
new Function("import('/api/hassio/app/frontend_latest/entrypoint.17d3e180.js')")();
} catch (err) {
var el = document.createElement('script');
el.src = '/api/hassio/app/frontend_es5/entrypoint.9603bcfc.js';
el.src = '/api/hassio/app/frontend_es5/entrypoint.1e6aec4d.js';
document.body.appendChild(el);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"chunk.165d124a6f3e8a41abb2.js","sources":["webpack://home-assistant-frontend/chunk.165d124a6f3e8a41abb2.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"chunk.581c71aa259dd86618ad.js","sources":["webpack://home-assistant-frontend/chunk.581c71aa259dd86618ad.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"chunk.ca8af569f5af755b6c71.js","sources":["webpack://home-assistant-frontend/chunk.ca8af569f5af755b6c71.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"chunk.d0222f0b152d21991ec4.js","sources":["webpack://home-assistant-frontend/chunk.d0222f0b152d21991ec4.js"],"mappings":"AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"entrypoint.1e6aec4d.js","sources":["webpack://home-assistant-frontend/entrypoint.1e6aec4d.js"],"mappings":";AAAA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"entrypoint.9603bcfc.js","sources":["webpack://home-assistant-frontend/entrypoint.9603bcfc.js"],"mappings":";AAAA","sourceRoot":""}

View File

@ -1,3 +1,3 @@
{
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.9603bcfc.js"
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.1e6aec4d.js"
}

View File

@ -1 +1 @@
{"version":3,"file":"chunk.608800a294b1d02a0765.js","sources":["webpack://home-assistant-frontend/chunk.608800a294b1d02a0765.js"],"mappings":"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","sourceRoot":""}
{"version":3,"file":"chunk.2ec57cdfeeb3748fc49f.js","sources":["webpack://home-assistant-frontend/chunk.2ec57cdfeeb3748fc49f.js"],"mappings":"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","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"chunk.c7a82086c5d680500dd2.js","sources":["webpack://home-assistant-frontend/chunk.c7a82086c5d680500dd2.js"],"mappings":"AAAA;;AAqMA;AACA;;AAEA;;AAEA;;AAEA;AAjBA;AACA;AACA;;AAEA;AAeA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyIA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"version":3,"file":"chunk.e24a51f3c66fd6498ace.js","sources":["webpack://home-assistant-frontend/chunk.e24a51f3c66fd6498ace.js"],"mappings":"AAAA;;AAyKA;AACA;;AAEA;;AAEA;;AAEA;AAjBA;AACA;AACA;;AAEA;AAeA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyIA","sourceRoot":""}

View File

@ -1,3 +1,3 @@
{
"entrypoint.js": "/api/hassio/app/frontend_latest/entrypoint.5c3eb78c.js"
"entrypoint.js": "/api/hassio/app/frontend_latest/entrypoint.17d3e180.js"
}

View File

@ -55,6 +55,9 @@ class Core(CoreSysAttributes):
)
finally:
self._state = new_state
self.sys_homeassistant.websocket.supervisor_update_event(
"info", {"state": new_state}
)
async def connect(self):
"""Connect Supervisor container."""

View File

@ -39,6 +39,14 @@ class HomeAssistantAuthError(HomeAssistantAPIError):
"""Home Assistant Auth API exception."""
class HomeAssistantWSError(HomeAssistantAPIError):
"""Home Assistant websocket error."""
class HomeAssistantWSNotSupported(HomeAssistantWSError):
"""Raise when WebSockets are not supported."""
class HomeAssistantJobError(HomeAssistantError, JobException):
"""Raise on Home Assistant job error."""

View File

@ -30,6 +30,7 @@ from ..validate import SCHEMA_HASS_CONFIG
from .api import HomeAssistantAPI
from .core import HomeAssistantCore
from .secrets import HomeAssistantSecrets
from .websocket import HomeAssistantWebSocket
_LOGGER: logging.Logger = logging.getLogger(__name__)
@ -42,6 +43,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG)
self.coresys: CoreSys = coresys
self._api: HomeAssistantAPI = HomeAssistantAPI(coresys)
self._websocket: HomeAssistantWebSocket = HomeAssistantWebSocket(coresys)
self._core: HomeAssistantCore = HomeAssistantCore(coresys)
self._secrets: HomeAssistantSecrets = HomeAssistantSecrets(coresys)
@ -50,6 +52,11 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
"""Return API handler for core."""
return self._api
@property
def websocket(self) -> HomeAssistantWebSocket:
"""Return Websocket handler for core."""
return self._websocket
@property
def core(self) -> HomeAssistantCore:
"""Return Core handler for docker."""

View File

@ -0,0 +1,167 @@
"""Home Assistant Websocket API."""
import logging
from typing import Any, Dict, Optional
import aiohttp
from awesomeversion import AwesomeVersion
from ..const import CoreState
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import (
HomeAssistantAPIError,
HomeAssistantWSError,
HomeAssistantWSNotSupported,
)
_LOGGER: logging.Logger = logging.getLogger(__name__)
CLOSING_STATES = [
CoreState.SHUTDOWN,
CoreState.STOPPING,
CoreState.CLOSE,
]
MIN_VERSION = {"supervisor/event": "2021.2.4"}
class WSClient:
"""Home Assistant Websocket client."""
def __init__(
self, ha_version: AwesomeVersion, client: aiohttp.ClientWebSocketResponse
):
"""Initialise the WS client."""
self.ha_version = ha_version
self.client = client
self.message_id = 0
async def async_send_command(self, message: Dict[str, Any]):
"""Send a websocket command."""
self.message_id += 1
message["id"] = self.message_id
_LOGGER.debug("Sending: %s", message)
try:
await self.client.send_json(message)
except HomeAssistantWSNotSupported:
return
response = await self.client.receive_json()
_LOGGER.debug("Received: %s", response)
if response["success"]:
return response["result"]
raise HomeAssistantWSError(response)
@classmethod
async def connect_with_auth(
cls, session: aiohttp.ClientSession, url: str, token: str
) -> "WSClient":
"""Create an authenticated websocket client."""
try:
client = await session.ws_connect(url)
except aiohttp.client_exceptions.ClientConnectorError:
raise HomeAssistantWSError("Can't connect") from None
hello_message = await client.receive_json()
try:
await client.send_json({"type": "auth", "access_token": token})
except HomeAssistantWSNotSupported:
return
auth_ok_message = await client.receive_json()
if auth_ok_message["type"] != "auth_ok":
raise HomeAssistantAPIError("AUTH NOT OK")
return cls(AwesomeVersion(hello_message["ha_version"]), client)
class HomeAssistantWebSocket(CoreSysAttributes):
"""Home Assistant Websocket API."""
def __init__(self, coresys: CoreSys):
"""Initialize Home Assistant object."""
self.coresys: CoreSys = coresys
self._client: Optional[WSClient] = None
async def _get_ws_client(self) -> WSClient:
"""Return a websocket client."""
await self.sys_homeassistant.api.ensure_access_token()
client = await WSClient.connect_with_auth(
self.sys_websession_ssl,
f"{self.sys_homeassistant.api_url}/api/websocket",
self.sys_homeassistant.api.access_token,
)
return client
async def async_send_command(self, message: Dict[str, Any]):
"""Send a command with the WS client."""
if self.sys_core.state in CLOSING_STATES:
raise HomeAssistantWSNotSupported(
f"Can't execute in a ${self.sys_core.state} state"
)
if not await self.sys_homeassistant.api.check_api_state():
# No core access, don't try.
return
if not self._client:
self._client = await self._get_ws_client()
message_type = message.get("type")
if (
message_type is not None
and message_type in MIN_VERSION
and self._client.ha_version < MIN_VERSION[message_type]
):
_LOGGER.info(
"WebSocket command %s is not supported untill core-%s. Ignoring WebSocket message.",
message_type,
MIN_VERSION[message_type],
)
return
try:
return await self._client.async_send_command(message)
except HomeAssistantAPIError as err:
raise HomeAssistantWSError from err
async def async_supervisor_update_event(
self, key: str, data: Optional[Dict[str, Any]] = None
):
"""Send a supervisor/event command."""
try:
await self.async_send_command(
{
"type": "supervisor/event",
"data": {
"event": "supervisor-update",
"update_key": key,
"data": data or {},
},
}
)
except HomeAssistantWSNotSupported:
pass
except HomeAssistantWSError as err:
_LOGGER.error(err)
def supervisor_update_event(self, key: str, data: Optional[Dict[str, Any]] = None):
"""Send a supervisor/event command."""
if self.sys_core.state not in CLOSING_STATES:
self.sys_loop.call_soon(
self.sys_loop.create_task,
self.async_supervisor_update_event(key, data),
)
def send_command(self, message: Dict[str, Any]):
"""Send a supervisor/event command."""
if self.sys_core.state not in CLOSING_STATES:
self.sys_loop.call_soon(
self.sys_loop.create_task, self.async_send_command(message)
)

View File

@ -6,6 +6,7 @@ from uuid import uuid4
from aiohttp import web
from aiohttp.test_utils import TestClient
from awesomeversion import AwesomeVersion
import pytest
from supervisor.api import RestAPI
@ -20,6 +21,12 @@ from tests.common import exists_fixture, load_fixture, load_json_fixture
# pylint: disable=redefined-outer-name, protected-access
async def mock_async_return_true() -> bool:
"""Mock methods to return True."""
return True
@pytest.fixture
def docker() -> DockerAPI:
"""Mock DockerAPI."""
@ -148,6 +155,12 @@ async def coresys(loop, docker, network_manager, aiohttp_client) -> CoreSys:
coresys_obj.supervisor._connectivity = True
coresys_obj.host.network._connectivity = True
# WebSocket
coresys_obj.homeassistant.api.check_api_state = mock_async_return_true
coresys_obj.homeassistant._websocket._client = AsyncMock(
ha_version=AwesomeVersion("2021.2.4")
)
yield coresys_obj

View File

@ -2,3 +2,4 @@
TEST_INTERFACE = "eth0"
TEST_INTERFACE_WLAN = "wlan0"
TEST_WS_URL = "ws://test.org:3000"

View File

@ -0,0 +1,49 @@
"""Test websocket."""
# pylint: disable=protected-access, import-error
import logging
from awesomeversion import AwesomeVersion
from supervisor.coresys import CoreSys
async def test_send_command(coresys: CoreSys):
"""Test websocket error on listen."""
client = coresys.homeassistant.websocket._client
await coresys.homeassistant.websocket.async_send_command({"type": "test"})
client.async_send_command.assert_called_with({"type": "test"})
await coresys.homeassistant.websocket.async_supervisor_update_event(
"test", {"lorem": "ipsum"}
)
client.async_send_command.assert_called_with(
{
"type": "supervisor/event",
"data": {
"event": "supervisor-update",
"update_key": "test",
"data": {"lorem": "ipsum"},
},
}
)
async def test_send_command_old_core_version(coresys: CoreSys, caplog):
"""Test websocket error on listen."""
caplog.set_level(logging.INFO)
client = coresys.homeassistant.websocket._client
client.ha_version = AwesomeVersion("1970.1.1")
await coresys.homeassistant.websocket.async_send_command(
{"type": "supervisor/event"}
)
assert (
"WebSocket command supervisor/event is not supported untill core-2021.2.4"
in caplog.text
)
await coresys.homeassistant.websocket.async_supervisor_update_event(
"test", {"lorem": "ipsum"}
)
client.async_send_command.assert_not_called()