2019-02-14 15:01:46 +00:00
|
|
|
"""Handler for Hass.io."""
|
2018-02-20 23:24:31 +00:00
|
|
|
import asyncio
|
2021-10-23 18:34:53 +00:00
|
|
|
from http import HTTPStatus
|
2018-02-20 23:24:31 +00:00
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
|
|
|
|
import aiohttp
|
|
|
|
|
|
|
|
from homeassistant.components.http import (
|
2019-03-11 02:55:36 +00:00
|
|
|
CONF_SERVER_HOST,
|
|
|
|
CONF_SERVER_PORT,
|
|
|
|
CONF_SSL_CERTIFICATE,
|
|
|
|
)
|
2021-10-23 18:34:53 +00:00
|
|
|
from homeassistant.const import SERVER_PORT
|
2018-02-20 23:24:31 +00:00
|
|
|
|
2018-10-11 08:55:38 +00:00
|
|
|
from .const import X_HASSIO
|
2018-02-20 23:24:31 +00:00
|
|
|
|
2018-10-11 08:55:38 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2018-02-20 23:24:31 +00:00
|
|
|
|
|
|
|
|
2018-10-03 11:10:38 +00:00
|
|
|
class HassioAPIError(RuntimeError):
|
|
|
|
"""Return if a API trow a error."""
|
|
|
|
|
|
|
|
|
2018-02-20 23:24:31 +00:00
|
|
|
def _api_bool(funct):
|
|
|
|
"""Return a boolean."""
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2018-07-23 12:14:57 +00:00
|
|
|
async def _wrapper(*argv, **kwargs):
|
2018-02-20 23:24:31 +00:00
|
|
|
"""Wrap function."""
|
2018-10-03 11:10:38 +00:00
|
|
|
try:
|
|
|
|
data = await funct(*argv, **kwargs)
|
2019-07-31 19:25:30 +00:00
|
|
|
return data["result"] == "ok"
|
2018-10-03 11:10:38 +00:00
|
|
|
except HassioAPIError:
|
|
|
|
return False
|
2018-02-20 23:24:31 +00:00
|
|
|
|
|
|
|
return _wrapper
|
|
|
|
|
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
def api_data(funct):
|
2018-05-13 10:09:28 +00:00
|
|
|
"""Return data of an api."""
|
2019-07-31 19:25:30 +00:00
|
|
|
|
2018-07-23 12:14:57 +00:00
|
|
|
async def _wrapper(*argv, **kwargs):
|
2018-02-21 21:42:55 +00:00
|
|
|
"""Wrap function."""
|
2018-07-23 12:14:57 +00:00
|
|
|
data = await funct(*argv, **kwargs)
|
2019-07-31 19:25:30 +00:00
|
|
|
if data["result"] == "ok":
|
|
|
|
return data["data"]
|
|
|
|
raise HassioAPIError(data["message"])
|
2018-02-21 21:42:55 +00:00
|
|
|
|
|
|
|
return _wrapper
|
|
|
|
|
|
|
|
|
2018-07-20 08:45:20 +00:00
|
|
|
class HassIO:
|
2018-02-20 23:24:31 +00:00
|
|
|
"""Small API wrapper for Hass.io."""
|
|
|
|
|
2021-03-15 18:31:34 +00:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
loop: asyncio.AbstractEventLoop,
|
|
|
|
websession: aiohttp.ClientSession,
|
|
|
|
ip: str,
|
|
|
|
) -> None:
|
2018-02-20 23:24:31 +00:00
|
|
|
"""Initialize Hass.io API."""
|
|
|
|
self.loop = loop
|
|
|
|
self.websession = websession
|
|
|
|
self._ip = ip
|
|
|
|
|
|
|
|
@_api_bool
|
|
|
|
def is_connected(self):
|
|
|
|
"""Return true if it connected to Hass.io supervisor.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
2019-03-26 14:38:25 +00:00
|
|
|
return self.send_command("/supervisor/ping", method="get", timeout=15)
|
2018-02-20 23:24:31 +00:00
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
@api_data
|
2020-05-12 22:27:34 +00:00
|
|
|
def get_info(self):
|
|
|
|
"""Return generic Supervisor information.
|
2018-02-20 23:24:31 +00:00
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
2020-05-12 22:27:34 +00:00
|
|
|
return self.send_command("/info", method="get")
|
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
@api_data
|
2020-05-12 22:27:34 +00:00
|
|
|
def get_host_info(self):
|
|
|
|
"""Return data for Host.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/host/info", method="get")
|
2018-02-20 23:24:31 +00:00
|
|
|
|
2020-11-11 19:12:24 +00:00
|
|
|
@api_data
|
|
|
|
def get_os_info(self):
|
|
|
|
"""Return data for the OS.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/os/info", method="get")
|
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
@api_data
|
2020-08-12 06:00:38 +00:00
|
|
|
def get_core_info(self):
|
|
|
|
"""Return data for Home Asssistant Core.
|
|
|
|
|
|
|
|
This method returns a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/core/info", method="get")
|
|
|
|
|
2020-11-11 19:12:24 +00:00
|
|
|
@api_data
|
|
|
|
def get_supervisor_info(self):
|
|
|
|
"""Return data for the Supervisor.
|
|
|
|
|
|
|
|
This method returns a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/supervisor/info", method="get")
|
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
@api_data
|
2018-10-03 11:10:38 +00:00
|
|
|
def get_addon_info(self, addon):
|
|
|
|
"""Return data for a Add-on.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
2019-08-23 16:53:33 +00:00
|
|
|
return self.send_command(f"/addons/{addon}/info", method="get")
|
2018-10-03 11:10:38 +00:00
|
|
|
|
2021-10-22 10:23:21 +00:00
|
|
|
@api_data
|
|
|
|
def get_addon_stats(self, addon):
|
|
|
|
"""Return stats for an Add-on.
|
|
|
|
|
|
|
|
This method returns a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command(f"/addons/{addon}/stats", method="get")
|
|
|
|
|
2021-05-31 12:06:11 +00:00
|
|
|
@api_data
|
|
|
|
def get_store(self):
|
|
|
|
"""Return data from the store.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/store", method="get")
|
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
@api_data
|
2019-04-19 07:43:47 +00:00
|
|
|
def get_ingress_panels(self):
|
|
|
|
"""Return data for Add-on ingress panels.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/ingress/panels", method="get")
|
|
|
|
|
2018-02-21 21:42:55 +00:00
|
|
|
@_api_bool
|
|
|
|
def restart_homeassistant(self):
|
|
|
|
"""Restart Home-Assistant container.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/homeassistant/restart")
|
|
|
|
|
|
|
|
@_api_bool
|
|
|
|
def stop_homeassistant(self):
|
|
|
|
"""Stop Home-Assistant container.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command("/homeassistant/stop")
|
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
@api_data
|
2018-10-03 11:10:38 +00:00
|
|
|
def retrieve_discovery_messages(self):
|
|
|
|
"""Return all discovery data from Hass.io API.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
2021-04-09 14:44:02 +00:00
|
|
|
return self.send_command("/discovery", method="get", timeout=60)
|
2018-10-03 11:10:38 +00:00
|
|
|
|
2020-10-15 21:38:53 +00:00
|
|
|
@api_data
|
2018-10-03 11:10:38 +00:00
|
|
|
def get_discovery_message(self, uuid):
|
|
|
|
"""Return a single discovery data message.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
2019-08-23 16:53:33 +00:00
|
|
|
return self.send_command(f"/discovery/{uuid}", method="get")
|
2018-10-03 11:10:38 +00:00
|
|
|
|
2018-02-20 23:24:31 +00:00
|
|
|
@_api_bool
|
2018-07-23 12:14:57 +00:00
|
|
|
async def update_hass_api(self, http_config, refresh_token):
|
|
|
|
"""Update Home Assistant API data on Hass.io."""
|
2018-02-20 23:24:31 +00:00
|
|
|
port = http_config.get(CONF_SERVER_PORT) or SERVER_PORT
|
|
|
|
options = {
|
2019-07-31 19:25:30 +00:00
|
|
|
"ssl": CONF_SSL_CERTIFICATE in http_config,
|
|
|
|
"port": port,
|
|
|
|
"watchdog": True,
|
2020-01-14 22:49:56 +00:00
|
|
|
"refresh_token": refresh_token.token,
|
2018-02-20 23:24:31 +00:00
|
|
|
}
|
|
|
|
|
2020-08-04 13:34:23 +00:00
|
|
|
if http_config.get(CONF_SERVER_HOST) is not None:
|
2019-07-31 19:25:30 +00:00
|
|
|
options["watchdog"] = False
|
2020-04-04 02:42:06 +00:00
|
|
|
_LOGGER.warning(
|
|
|
|
"Found incompatible HTTP option 'server_host'. Watchdog feature disabled"
|
|
|
|
)
|
2018-02-20 23:24:31 +00:00
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
return await self.send_command("/homeassistant/options", payload=options)
|
2018-02-20 23:24:31 +00:00
|
|
|
|
|
|
|
@_api_bool
|
2019-06-10 23:05:43 +00:00
|
|
|
def update_hass_timezone(self, timezone):
|
2018-02-20 23:24:31 +00:00
|
|
|
"""Update Home-Assistant timezone data on Hass.io.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
2019-07-31 19:25:30 +00:00
|
|
|
return self.send_command("/supervisor/options", payload={"timezone": timezone})
|
2018-02-20 23:24:31 +00:00
|
|
|
|
2021-03-30 00:20:11 +00:00
|
|
|
@_api_bool
|
|
|
|
def update_diagnostics(self, diagnostics: bool):
|
|
|
|
"""Update Supervisor diagnostics setting.
|
|
|
|
|
|
|
|
This method return a coroutine.
|
|
|
|
"""
|
|
|
|
return self.send_command(
|
|
|
|
"/supervisor/options", payload={"diagnostics": diagnostics}
|
|
|
|
)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
async def send_command(self, command, method="post", payload=None, timeout=10):
|
2018-02-20 23:24:31 +00:00
|
|
|
"""Send API command to Hass.io.
|
|
|
|
|
|
|
|
This method is a coroutine.
|
|
|
|
"""
|
|
|
|
try:
|
2021-03-15 18:31:34 +00:00
|
|
|
request = await self.websession.request(
|
|
|
|
method,
|
|
|
|
f"http://{self._ip}{command}",
|
|
|
|
json=payload,
|
|
|
|
headers={X_HASSIO: os.environ.get("HASSIO_TOKEN", "")},
|
|
|
|
timeout=aiohttp.ClientTimeout(total=timeout),
|
|
|
|
)
|
|
|
|
|
2021-10-23 18:34:53 +00:00
|
|
|
if request.status not in (HTTPStatus.OK, HTTPStatus.BAD_REQUEST):
|
2021-03-15 18:31:34 +00:00
|
|
|
_LOGGER.error("%s return code %d", command, request.status)
|
|
|
|
raise HassioAPIError()
|
|
|
|
|
|
|
|
answer = await request.json()
|
|
|
|
return answer
|
2018-02-20 23:24:31 +00:00
|
|
|
|
|
|
|
except asyncio.TimeoutError:
|
|
|
|
_LOGGER.error("Timeout on %s request", command)
|
|
|
|
|
|
|
|
except aiohttp.ClientError as err:
|
|
|
|
_LOGGER.error("Client error on %s request %s", command, err)
|
|
|
|
|
2018-10-03 11:10:38 +00:00
|
|
|
raise HassioAPIError()
|