From 90d8832cd29e0aa69b01fa90c176aa16a638f0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 23 Feb 2021 15:12:30 +0100 Subject: [PATCH] Send event when add-on changes state (#2608) * Send event when add-on changes state * fix test --- supervisor/addons/addon.py | 29 ++++++++++++++++++- supervisor/const.py | 41 ++++++++++++++------------- supervisor/core.py | 2 ++ supervisor/homeassistant/const.py | 26 +++++++++++++++++ supervisor/homeassistant/websocket.py | 25 ++++++---------- tests/homeassistant/test_websocket.py | 5 ++-- 6 files changed, 90 insertions(+), 38 deletions(-) create mode 100644 supervisor/homeassistant/const.py diff --git a/supervisor/addons/addon.py b/supervisor/addons/addon.py index ff47fde05..ec1a05f9f 100644 --- a/supervisor/addons/addon.py +++ b/supervisor/addons/addon.py @@ -22,6 +22,8 @@ from ..const import ( ATTR_AUDIO_OUTPUT, ATTR_AUTO_UPDATE, ATTR_BOOT, + ATTR_DATA, + ATTR_EVENT, ATTR_IMAGE, ATTR_INGRESS_ENTRY, ATTR_INGRESS_PANEL, @@ -32,8 +34,10 @@ from ..const import ( ATTR_PORTS, ATTR_PROTECTED, ATTR_SCHEMA, + ATTR_SLUG, ATTR_STATE, ATTR_SYSTEM, + ATTR_TYPE, ATTR_USER, ATTR_UUID, ATTR_VERSION, @@ -56,6 +60,7 @@ from ..exceptions import ( JsonFileError, ) from ..hardware.data import Device +from ..homeassistant.const import WSEvent, WSType from ..utils import check_port from ..utils.apparmor import adjust_profile from ..utils.json import read_json_file, write_json_file @@ -89,12 +94,34 @@ class Addon(AddonModel): """Initialize data holder.""" super().__init__(coresys, slug) self.instance: DockerAddon = DockerAddon(coresys, self) - self.state: AddonState = AddonState.UNKNOWN + self._state: AddonState = AddonState.UNKNOWN def __repr__(self) -> str: """Return internal representation.""" return f"" + @property + def state(self) -> AddonState: + """Return state of the add-on.""" + return self._state + + @state.setter + def state(self, new_state: AddonState) -> None: + """Set the add-on into new state.""" + if self._state == new_state: + return + self._state = new_state + self.sys_homeassistant.websocket.send_command( + { + ATTR_TYPE: WSType.SUPERVISOR_EVENT, + ATTR_DATA: { + ATTR_EVENT: WSEvent.ADDON, + ATTR_SLUG: self.slug, + ATTR_STATE: new_state, + }, + } + ) + @property def in_progress(self) -> bool: """Return True if a task is in progress.""" diff --git a/supervisor/const.py b/supervisor/const.py index 6a3ac4f0a..6bafb329c 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -73,6 +73,7 @@ ENV_SUPERVISOR_CPU_RT = "SUPERVISOR_CPU_RT" REQUEST_FROM = "HASSIO_FROM" ATTR_ACCESS_TOKEN = "access_token" +ATTR_ACCESSPOINTS = "accesspoints" ATTR_ACTIVE = "active" ATTR_ADDON = "addon" ATTR_ADDONS = "addons" @@ -89,8 +90,8 @@ ATTR_ARGS = "args" ATTR_AUDIO = "audio" ATTR_AUDIO_INPUT = "audio_input" ATTR_AUDIO_OUTPUT = "audio_output" +ATTR_AUTH = "auth" ATTR_AUTH_API = "auth_api" -ATTR_UART = "uart" ATTR_AUTO_UPDATE = "auto_update" ATTR_AVAILABLE = "available" ATTR_BLK_READ = "blk_read" @@ -106,11 +107,13 @@ ATTR_CHANNEL = "channel" ATTR_CHASSIS = "chassis" ATTR_CLI = "cli" ATTR_CONFIG = "config" +ATTR_CONNECTED = "connected" ATTR_CONNECTIONS = "connections" ATTR_CONTAINERS = "containers" ATTR_CPE = "cpe" ATTR_CPU_PERCENT = "cpu_percent" ATTR_CRYPTO = "crypto" +ATTR_DATA = "data" ATTR_DATE = "date" ATTR_DEBUG = "debug" ATTR_DEBUG_BLOCK = "debug_block" @@ -124,6 +127,7 @@ ATTR_DIAGNOSTICS = "diagnostics" ATTR_DISCOVERY = "discovery" ATTR_DISK = "disk" ATTR_DISK_FREE = "disk_free" +ATTR_DISK_LIFE_TIME = "disk_life_time" ATTR_DISK_TOTAL = "disk_total" ATTR_DISK_USED = "disk_used" ATTR_DNS = "dns" @@ -132,11 +136,14 @@ ATTR_DOCKER_API = "docker_api" ATTR_DOCUMENTATION = "documentation" ATTR_DOMAINS = "domains" ATTR_ENABLE = "enable" +ATTR_ENABLED = "enabled" ATTR_ENVIRONMENT = "environment" +ATTR_EVENT = "event" ATTR_FEATURES = "features" ATTR_FILENAME = "filename" ATTR_FLAGS = "flags" ATTR_FOLDERS = "folders" +ATTR_FREQUENCY = "frequency" ATTR_FULL_ACCESS = "full_access" ATTR_GATEWAY = "gateway" ATTR_GPIO = "gpio" @@ -155,7 +162,6 @@ ATTR_HOST_PID = "host_pid" ATTR_HOSTNAME = "hostname" ATTR_ICON = "icon" ATTR_ID = "id" -ATTR_ISSUES = "issues" ATTR_IMAGE = "image" ATTR_IMAGES = "images" ATTR_INDEX = "index" @@ -174,6 +180,7 @@ ATTR_INTERFACES = "interfaces" ATTR_IP_ADDRESS = "ip_address" ATTR_IPV4 = "ipv4" ATTR_IPV6 = "ipv6" +ATTR_ISSUES = "issues" ATTR_KERNEL = "kernel" ATTR_KERNEL_MODULES = "kernel_modules" ATTR_LAST_BOOT = "last_boot" @@ -183,6 +190,7 @@ ATTR_LOCATON = "location" ATTR_LOGGING = "logging" ATTR_LOGO = "logo" ATTR_LONG_DESCRIPTION = "long_description" +ATTR_MAC = "mac" ATTR_MACHINE = "machine" ATTR_MAINTAINER = "maintainer" ATTR_MAP = "map" @@ -199,15 +207,17 @@ ATTR_NETWORK = "network" ATTR_NETWORK_DESCRIPTION = "network_description" ATTR_NETWORK_RX = "network_rx" ATTR_NETWORK_TX = "network_tx" +ATTR_OBSERVER = "observer" ATTR_OPERATING_SYSTEM = "operating_system" ATTR_OPTIONS = "options" +ATTR_OTA = "ota" ATTR_OUTPUT = "output" ATTR_PANEL_ADMIN = "panel_admin" ATTR_PANEL_ICON = "panel_icon" ATTR_PANEL_TITLE = "panel_title" ATTR_PANELS = "panels" -ATTR_PASSWORD = "password" ATTR_PARENT = "parent" +ATTR_PASSWORD = "password" ATTR_PORT = "port" ATTR_PORTS = "ports" ATTR_PORTS_DESCRIPTION = "ports_description" @@ -217,7 +227,9 @@ ATTR_PRIORITY = "priority" ATTR_PRIVILEGED = "privileged" ATTR_PROTECTED = "protected" ATTR_PROVIDERS = "providers" +ATTR_PSK = "psk" ATTR_RATING = "rating" +ATTR_REALTIME = "realtime" ATTR_REFRESH_TOKEN = "refresh_token" ATTR_REGISTRIES = "registries" ATTR_REGISTRY = "registry" @@ -230,14 +242,15 @@ ATTR_SERVERS = "servers" ATTR_SERVICE = "service" ATTR_SERVICES = "services" ATTR_SESSION = "session" +ATTR_SIGNAL = "signal" ATTR_SIZE = "size" ATTR_SLUG = "slug" ATTR_SNAPSHOT_EXCLUDE = "snapshot_exclude" ATTR_SNAPSHOTS = "snapshots" ATTR_SOURCE = "source" ATTR_SQUASH = "squash" +ATTR_SSD = "ssid" ATTR_SSID = "ssid" -ATTR_DISK_LIFE_TIME = "disk_life_time" ATTR_SSL = "ssl" ATTR_STAGE = "stage" ATTR_STARTUP = "startup" @@ -257,9 +270,13 @@ ATTR_TITLE = "title" ATTR_TMPFS = "tmpfs" ATTR_TOTP = "totp" ATTR_TYPE = "type" +ATTR_UART = "uart" ATTR_UDEV = "udev" +ATTR_UNHEALTHY = "unhealthy" ATTR_UNSAVED = "unsaved" ATTR_UNSUPPORTED = "unsupported" +ATTR_UPDATE_AVAILABLE = "update_available" +ATTR_UPDATE_KEY = "update_key" ATTR_URL = "url" ATTR_USB = "usb" ATTR_USER = "user" @@ -270,27 +287,13 @@ ATTR_VALUE = "value" ATTR_VERSION = "version" ATTR_VERSION_LATEST = "version_latest" ATTR_VIDEO = "video" +ATTR_VLAN = "vlan" ATTR_VOLUME = "volume" ATTR_VPN = "vpn" ATTR_WAIT_BOOT = "wait_boot" ATTR_WATCHDOG = "watchdog" ATTR_WEBUI = "webui" -ATTR_OBSERVER = "observer" -ATTR_UPDATE_AVAILABLE = "update_available" ATTR_WIFI = "wifi" -ATTR_VLAN = "vlan" -ATTR_SSD = "ssid" -ATTR_AUTH = "auth" -ATTR_PSK = "psk" -ATTR_CONNECTED = "connected" -ATTR_ENABLED = "enabled" -ATTR_SIGNAL = "signal" -ATTR_MAC = "mac" -ATTR_FREQUENCY = "frequency" -ATTR_ACCESSPOINTS = "accesspoints" -ATTR_UNHEALTHY = "unhealthy" -ATTR_OTA = "ota" -ATTR_REALTIME = "realtime" PROVIDE_SERVICE = "provide" NEED_SERVICE = "need" diff --git a/supervisor/core.py b/supervisor/core.py index c4aab3ac6..77c79aed6 100644 --- a/supervisor/core.py +++ b/supervisor/core.py @@ -47,6 +47,8 @@ class Core(CoreSysAttributes): @state.setter def state(self, new_state: CoreState) -> None: """Set core into new state.""" + if self._state == new_state: + return try: RUN_SUPERVISOR_STATE.write_text(new_state.value) except OSError as err: diff --git a/supervisor/homeassistant/const.py b/supervisor/homeassistant/const.py new file mode 100644 index 000000000..faf690726 --- /dev/null +++ b/supervisor/homeassistant/const.py @@ -0,0 +1,26 @@ +"""Constants for homeassistant.""" +from enum import Enum + +from ..const import CoreState + +MIN_VERSION = {"supervisor/event": "2021.2.4"} + +CLOSING_STATES = [ + CoreState.SHUTDOWN, + CoreState.STOPPING, + CoreState.CLOSE, +] + + +class WSType(str, Enum): + """Websocket types.""" + + AUTH = "auth" + SUPERVISOR_EVENT = "supervisor/event" + + +class WSEvent(str, Enum): + """Websocket events.""" + + ADDON = "addon" + SUPERVISOR_UPDATE = "supervisor_update" diff --git a/supervisor/homeassistant/websocket.py b/supervisor/homeassistant/websocket.py index 133dc0162..5826564e0 100644 --- a/supervisor/homeassistant/websocket.py +++ b/supervisor/homeassistant/websocket.py @@ -5,24 +5,17 @@ from typing import Any, Dict, Optional import aiohttp from awesomeversion import AwesomeVersion -from ..const import CoreState +from ..const import ATTR_ACCESS_TOKEN, ATTR_DATA, ATTR_EVENT, ATTR_TYPE, ATTR_UPDATE_KEY from ..coresys import CoreSys, CoreSysAttributes from ..exceptions import ( HomeAssistantAPIError, HomeAssistantWSError, HomeAssistantWSNotSupported, ) +from .const import CLOSING_STATES, MIN_VERSION, WSEvent, WSType _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.""" @@ -68,13 +61,13 @@ class WSClient: hello_message = await client.receive_json() try: - await client.send_json({"type": "auth", "access_token": token}) + await client.send_json({ATTR_TYPE: WSType.AUTH, ATTR_ACCESS_TOKEN: token}) except HomeAssistantWSNotSupported: return auth_ok_message = await client.receive_json() - if auth_ok_message["type"] != "auth_ok": + if auth_ok_message[ATTR_TYPE] != "auth_ok": raise HomeAssistantAPIError("AUTH NOT OK") return cls(AwesomeVersion(hello_message["ha_version"]), client) @@ -138,11 +131,11 @@ class HomeAssistantWebSocket(CoreSysAttributes): try: await self.async_send_command( { - "type": "supervisor/event", - "data": { - "event": "supervisor-update", - "update_key": key, - "data": data or {}, + ATTR_TYPE: WSType.SUPERVISOR_EVENT, + ATTR_DATA: { + ATTR_EVENT: WSEvent.SUPERVISOR_UPDATE, + ATTR_UPDATE_KEY: key, + ATTR_DATA: data or {}, }, } ) diff --git a/tests/homeassistant/test_websocket.py b/tests/homeassistant/test_websocket.py index 9d31428c8..18f28c8b8 100644 --- a/tests/homeassistant/test_websocket.py +++ b/tests/homeassistant/test_websocket.py @@ -5,6 +5,7 @@ import logging from awesomeversion import AwesomeVersion from supervisor.coresys import CoreSys +from supervisor.homeassistant.const import WSEvent, WSType async def test_send_command(coresys: CoreSys): @@ -18,9 +19,9 @@ async def test_send_command(coresys: CoreSys): ) client.async_send_command.assert_called_with( { - "type": "supervisor/event", + "type": WSType.SUPERVISOR_EVENT, "data": { - "event": "supervisor-update", + "event": WSEvent.SUPERVISOR_UPDATE, "update_key": "test", "data": {"lorem": "ipsum"}, },