2019-02-14 15:01:46 +00:00
|
|
|
"""Implement the services discovery feature from Hass.io for Add-ons."""
|
2021-12-02 19:17:54 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2018-10-03 11:10:38 +00:00
|
|
|
import asyncio
|
2021-12-02 19:17:54 +00:00
|
|
|
from dataclasses import dataclass
|
2018-10-03 11:10:38 +00:00
|
|
|
import logging
|
2021-12-02 19:17:54 +00:00
|
|
|
from typing import Any
|
2018-10-03 11:10:38 +00:00
|
|
|
|
|
|
|
from aiohttp import web
|
|
|
|
from aiohttp.web_exceptions import HTTPServiceUnavailable
|
|
|
|
|
2021-04-25 09:27:40 +00:00
|
|
|
from homeassistant import config_entries
|
2019-12-08 15:33:22 +00:00
|
|
|
from homeassistant.components.http import HomeAssistantView
|
2021-02-23 08:56:44 +00:00
|
|
|
from homeassistant.const import ATTR_NAME, ATTR_SERVICE, EVENT_HOMEASSISTANT_START
|
2021-08-21 11:58:49 +00:00
|
|
|
from homeassistant.core import HomeAssistant, callback
|
2021-12-02 19:17:54 +00:00
|
|
|
from homeassistant.data_entry_flow import BaseServiceInfo
|
2018-10-03 11:10:38 +00:00
|
|
|
|
2021-02-23 08:56:44 +00:00
|
|
|
from .const import ATTR_ADDON, ATTR_CONFIG, ATTR_DISCOVERY, ATTR_UUID
|
2019-02-14 15:01:46 +00:00
|
|
|
from .handler import HassioAPIError
|
2018-10-03 11:10:38 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2021-12-02 19:17:54 +00:00
|
|
|
@dataclass
|
|
|
|
class HassioServiceInfo(BaseServiceInfo):
|
|
|
|
"""Prepared info from hassio entries."""
|
|
|
|
|
2021-12-03 13:05:56 +00:00
|
|
|
config: dict[str, Any]
|
2021-12-02 19:17:54 +00:00
|
|
|
|
|
|
|
|
2018-10-03 11:10:38 +00:00
|
|
|
@callback
|
2021-08-21 11:58:49 +00:00
|
|
|
def async_setup_discovery_view(hass: HomeAssistant, hassio):
|
2018-10-03 11:10:38 +00:00
|
|
|
"""Discovery setup."""
|
2019-04-19 07:43:47 +00:00
|
|
|
hassio_discovery = HassIODiscovery(hass, hassio)
|
|
|
|
hass.http.register_view(hassio_discovery)
|
2018-10-03 11:10:38 +00:00
|
|
|
|
|
|
|
# Handle exists discovery messages
|
2019-04-19 07:43:47 +00:00
|
|
|
async def _async_discovery_start_handler(event):
|
2018-10-03 11:10:38 +00:00
|
|
|
"""Process all exists discovery on startup."""
|
|
|
|
try:
|
|
|
|
data = await hassio.retrieve_discovery_messages()
|
|
|
|
except HassioAPIError as err:
|
|
|
|
_LOGGER.error("Can't read discover info: %s", err)
|
|
|
|
return
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
jobs = [
|
|
|
|
hassio_discovery.async_process_new(discovery)
|
|
|
|
for discovery in data[ATTR_DISCOVERY]
|
|
|
|
]
|
2018-10-03 11:10:38 +00:00
|
|
|
if jobs:
|
|
|
|
await asyncio.wait(jobs)
|
|
|
|
|
2019-04-19 07:43:47 +00:00
|
|
|
hass.bus.async_listen_once(
|
2019-07-31 19:25:30 +00:00
|
|
|
EVENT_HOMEASSISTANT_START, _async_discovery_start_handler
|
|
|
|
)
|
2018-10-03 11:10:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HassIODiscovery(HomeAssistantView):
|
|
|
|
"""Hass.io view to handle base part."""
|
|
|
|
|
|
|
|
name = "api:hassio_push:discovery"
|
|
|
|
url = "/api/hassio_push/discovery/{uuid}"
|
|
|
|
|
2021-08-21 11:58:49 +00:00
|
|
|
def __init__(self, hass: HomeAssistant, hassio):
|
2018-10-03 11:10:38 +00:00
|
|
|
"""Initialize WebView."""
|
|
|
|
self.hass = hass
|
|
|
|
self.hassio = hassio
|
|
|
|
|
|
|
|
async def post(self, request, uuid):
|
|
|
|
"""Handle new discovery requests."""
|
|
|
|
# Fetch discovery data and prevent injections
|
|
|
|
try:
|
|
|
|
data = await self.hassio.get_discovery_message(uuid)
|
|
|
|
except HassioAPIError as err:
|
2020-08-08 12:41:02 +00:00
|
|
|
_LOGGER.error("Can't read discovery data: %s", err)
|
2018-10-03 11:10:38 +00:00
|
|
|
raise HTTPServiceUnavailable() from None
|
|
|
|
|
|
|
|
await self.async_process_new(data)
|
|
|
|
return web.Response()
|
|
|
|
|
|
|
|
async def delete(self, request, uuid):
|
|
|
|
"""Handle remove discovery requests."""
|
2018-11-04 11:19:04 +00:00
|
|
|
data = await request.json()
|
2018-10-03 11:10:38 +00:00
|
|
|
|
|
|
|
await self.async_process_del(data)
|
|
|
|
return web.Response()
|
|
|
|
|
|
|
|
async def async_process_new(self, data):
|
|
|
|
"""Process add discovery entry."""
|
|
|
|
service = data[ATTR_SERVICE]
|
|
|
|
config_data = data[ATTR_CONFIG]
|
|
|
|
|
2019-04-03 02:23:33 +00:00
|
|
|
# Read additional Add-on info
|
2018-10-03 11:10:38 +00:00
|
|
|
try:
|
|
|
|
addon_info = await self.hassio.get_addon_info(data[ATTR_ADDON])
|
|
|
|
except HassioAPIError as err:
|
|
|
|
_LOGGER.error("Can't read add-on info: %s", err)
|
|
|
|
return
|
|
|
|
config_data[ATTR_ADDON] = addon_info[ATTR_NAME]
|
|
|
|
|
|
|
|
# Use config flow
|
|
|
|
await self.hass.config_entries.flow.async_init(
|
2021-12-03 13:05:56 +00:00
|
|
|
service,
|
|
|
|
context={"source": config_entries.SOURCE_HASSIO},
|
|
|
|
data=HassioServiceInfo(config=config_data),
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2018-10-03 11:10:38 +00:00
|
|
|
|
|
|
|
async def async_process_del(self, data):
|
|
|
|
"""Process remove discovery entry."""
|
|
|
|
service = data[ATTR_SERVICE]
|
|
|
|
uuid = data[ATTR_UUID]
|
|
|
|
|
2019-04-03 02:23:33 +00:00
|
|
|
# Check if really deletet / prevent injections
|
2018-10-03 11:10:38 +00:00
|
|
|
try:
|
|
|
|
data = await self.hassio.get_discovery_message(uuid)
|
|
|
|
except HassioAPIError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
_LOGGER.warning("Retrieve wrong unload for %s", service)
|
|
|
|
return
|
|
|
|
|
|
|
|
# Use config flow
|
|
|
|
for entry in self.hass.config_entries.async_entries(service):
|
2021-04-25 09:27:40 +00:00
|
|
|
if entry.source != config_entries.SOURCE_HASSIO:
|
2018-10-03 11:10:38 +00:00
|
|
|
continue
|
|
|
|
await self.hass.config_entries.async_remove(entry)
|