diff --git a/custom_components/sysdweb/__init__.py b/custom_components/sysdweb/__init__.py new file mode 100644 index 0000000..a432081 --- /dev/null +++ b/custom_components/sysdweb/__init__.py @@ -0,0 +1,55 @@ +import logging +import voluptuous as vol +from collections import defaultdict +import homeassistant.helpers.config_validation as cv +from homeassistant.const import CONF_HOST, CONF_PORT, CONF_USERNAME, CONF_PASSWORD +from homeassistant.helpers import aiohttp_client +import functools +from aiohttp import BasicAuth + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = 'sysdweb' +DATA_SYSDWEB = "data_sysdweb" +CONF_SERVICES = "services" + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: cv.ensure_list(vol.Schema({ + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_PORT, default=10080): cv.port, + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Required(CONF_SERVICES): cv.ensure_list(cv.string) + })) +}, extra=vol.ALLOW_EXTRA) + + +async def async_setup(hass, config): + info_map = {} + for conf_entry in config[DOMAIN]: + host = conf_entry[CONF_HOST] + port = conf_entry[CONF_PORT] + auth = BasicAuth(login=conf_entry[CONF_USERNAME], password=conf_entry[CONF_PASSWORD]) + for service_name in conf_entry[CONF_SERVICES]: + info_map[(host, service_name)] = (f"http://{host}:{port}/api/v1/{service_name}/", auth) + hass.data[DATA_SYSDWEB] = info_map + + # services + possible_actions = ['start', 'stop', 'restart', 'reload', 'reloadorrestart'] + + async def service_call(call, sysdweb_action): + assert sysdweb_action in possible_actions + hostname = call.data.get("hostname") + service_name = call.data.get("service_name") + url, auth = hass.data[DATA_SYSDWEB][(hostname, service_name)] + session = aiohttp_client.async_get_clientsession(hass) + async with session.get(url + sysdweb_action, auth=auth) as resp: + resp_json = await resp.json() + result = resp_json[sysdweb_action] + if result != 'OK' or resp.status != 200: + _LOGGER.error(f"Error calling service sysdweb: {action} {resp_json}") + + for action in possible_actions: + hass.services.async_register(DOMAIN, action, functools.partial(service_call, sysdweb_action=action)) + + return True diff --git a/custom_components/sysdweb/binary_sensor.py b/custom_components/sysdweb/binary_sensor.py new file mode 100644 index 0000000..5641aa8 --- /dev/null +++ b/custom_components/sysdweb/binary_sensor.py @@ -0,0 +1,67 @@ +from . import DATA_SYSDWEB +import logging +from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.helpers import aiohttp_client + +_LOGGER = logging.getLogger(__name__) + + +class ServiceStateSensor(BinarySensorDevice): + + def __init__(self, name, url, auth): + self._on = None + self._name = name + self._available = False + self._url = url + self._auth = auth + self._status = 'unknown' + + @property + def device_state_attributes(self): + return {'status': self._state} + + @property + def name(self): + return self._name + + @property + def available(self) -> bool: + return self._available + + @property + def is_on(self): + return self._on + + async def async_update(self): + session = aiohttp_client.async_get_clientsession(self.hass) + async with session.get(self._url, auth=self._auth) as resp: + resp_json = await resp.json() + status_result = resp_json["status"] + self._state = status_result + if resp.status == 200: + if status_result == 'active': + self._on = True + self._available = True + elif status_result == 'not-found': + self._on = None + self._available = False + else: + self._on = False + self._available = True + else: + self._available = False + self._state = "unknown" + _LOGGER.error(f"Error calling service sysdweb status {self._url}: {resp_json}") + + +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): + data = hass.data[DATA_SYSDWEB] + sensors = [] + for (host, service_name), (url, auth) in data.items(): + host_simple = host + + if '.' in host_simple: + host_simple = host_simple.split('.')[0] + sensor_name = f"sysdweb_{host_simple}_{service_name}" + sensors.append(ServiceStateSensor(sensor_name, url + "status", auth)) + async_add_entities(sensors) diff --git a/custom_components/sysdweb/sensor.py b/custom_components/sysdweb/sensor.py new file mode 100644 index 0000000..e69de29 diff --git a/custom_components/sysdweb/services.yaml b/custom_components/sysdweb/services.yaml new file mode 100644 index 0000000..1dc3e37 --- /dev/null +++ b/custom_components/sysdweb/services.yaml @@ -0,0 +1,49 @@ +start: + description: Starts a service via sysdweb + fields: + hostname: + description: hostname where the service should be controlled + example: "kitchenpi.fritz.box" + service_name: + description: Name of the service + example: "lircd" + +stop: + description: Stops a service via sysdweb + fields: + hostname: + description: hostname where the service should be controlled + example: "kitchenpi.fritz.box" + service_name: + description: Name of the service + example: "lircd" + +restart: + description: Restarts a service via sysdweb + fields: + hostname: + description: hostname where the service should be controlled + example: "kitchenpi.fritz.box" + service_name: + description: Name of the service + example: "lircd" + +reload: + description: Reloads a service via sysdweb + fields: + hostname: + description: hostname where the service should be controlled + example: "kitchenpi.fritz.box" + service_name: + description: Name of the service + example: "lircd" + +reloadorrestart: + description: Reloads or restarts a service via sysdweb + fields: + hostname: + description: hostname where the service should be controlled + example: "kitchenpi.fritz.box" + service_name: + description: Name of the service + example: "lircd"