supervisor/hassio/discovery.py
2018-10-01 18:57:31 +02:00

122 lines
3.9 KiB
Python

"""Handle discover message for Home Assistant."""
import logging
from contextlib import suppress
from uuid import uuid4
import attr
import voluptuous as vol
from voluptuous.humanize import humanize_error
from .const import FILE_HASSIO_DISCOVERY, ATTR_CONFIG, ATTR_DISCOVERY
from .coresys import CoreSysAttributes
from .exceptions import DiscoveryError, HomeAssistantAPIError
from .validate import SCHEMA_DISCOVERY_CONFIG
from .utils.json import JsonConfig
from .services.validate import DISCOVERY_SERVICES
_LOGGER = logging.getLogger(__name__)
CMD_NEW = 'post'
CMD_DEL = 'delete'
class Discovery(CoreSysAttributes, JsonConfig):
"""Home Assistant Discovery handler."""
def __init__(self, coresys):
"""Initialize discovery handler."""
super().__init__(FILE_HASSIO_DISCOVERY, SCHEMA_DISCOVERY_CONFIG)
self.coresys = coresys
self.message_obj = {}
async def load(self):
"""Load exists discovery message into storage."""
messages = {}
for message in self._data[ATTR_DISCOVERY]:
discovery = Message(**message)
messages[discovery.uuid] = discovery
self.message_obj = messages
def save(self):
"""Write discovery message into data file."""
messages = []
for message in self.list_messages:
messages.append(attr.asdict(message))
self._data[ATTR_DISCOVERY].clear()
self._data[ATTR_DISCOVERY].extend(messages)
self.save_data()
def get(self, uuid):
"""Return discovery message."""
return self.message_obj.get(uuid)
@property
def list_messages(self):
"""Return list of available discovery messages."""
return list(self.message_obj.values())
def send(self, addon, service, config):
"""Send a discovery message to Home Assistant."""
try:
DISCOVERY_SERVICES[service](config)
except vol.Invalid as err:
_LOGGER.error(
"Invalid discovery %s config", humanize_error(config, err))
raise DiscoveryError() from None
# Create message
message = Message(addon.slug, service, config)
# Already exists?
for old_message in self.list_messages:
if old_message != message:
continue
_LOGGER.warning("Duplicate discovery message from %s", addon.slug)
return old_message
_LOGGER.info("Send discovery to Home Assistant %s from %s",
service, addon.slug)
self.message_obj[message.uuid] = message
self.save()
self.sys_create_task(self._push_discovery(message, CMD_NEW))
return message
def remove(self, message):
"""Remove a discovery message from Home Assistant."""
self.message_obj.pop(message.uuid, None)
self.save()
_LOGGER.info("Delete discovery to Home Assistant %s from %s",
message.service, message.addon)
self.sys_create_task(self._push_discovery(message, CMD_DEL))
async def _push_discovery(self, message, command):
"""Send a discovery request."""
if not await self.sys_homeassistant.check_api_state():
_LOGGER.info("Discovery %s mesage ignore", message.uuid)
return
data = attr.asdict(message)
data.pop(ATTR_CONFIG)
with suppress(HomeAssistantAPIError):
async with self.sys_homeassistant.make_request(
command, f"api/hassio_push/discovery/{message.uuid}",
json=data, timeout=10):
_LOGGER.info("Discovery %s message send", message.uuid)
return
_LOGGER.warning("Discovery %s message fail", message.uuid)
@attr.s
class Message:
"""Represent a single Discovery message."""
uuid = attr.ib(factory=lambda: uuid4().hex, cmp=False, init=False)
addon = attr.ib()
service = attr.ib()
config = attr.ib(cmp=False)