XS1 component (#19115)
* added xs1 main component added implementations for switch, sensor, climate and binary_sensor * updated code fixed styling added comments removed binary_sensor (wasn't working) * ran "gen_requirements_all.py" script * fixed linting issues * added config options for port and ssl small fixes * use already defined config constants instead of defining new ones * avoid passing in hass to the entity * use async keyword and proper asyncio calls limit updates with a global lock to prevent overwhelming the gateway with concurrent requests change info logger calls to debug * update dependency * removed unneeded constant * fix lint issues * updated requirements * removed unused imports * fixed some flake8 errors * fixed some flake8 errors * changed imports to absolute paths * fixed some lint errors * fixed some lint errors * fix update of attached sensor * reordered imports added config defaults check if platform is available changed docstring * lint fix * review fixes * isort * import fix * review fix * review fix * review fix * removed unused imports * lint fix * lint fix * climate fix exclude sensors that will be used in climate component from default sensor category * .coveragerc fix * lint fix * moved platform to it's own packagepull/20993/head
parent
a9672b0d52
commit
542f024356
|
@ -655,6 +655,7 @@ omit =
|
|||
homeassistant/components/wirelesstag/*
|
||||
homeassistant/components/xiaomi_aqara/*
|
||||
homeassistant/components/xiaomi_miio/*
|
||||
homeassistant/components/xs1/*
|
||||
homeassistant/components/zabbix/*
|
||||
homeassistant/components/zeroconf/*
|
||||
homeassistant/components/zha/__init__.py
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
"""
|
||||
Support for the EZcontrol XS1 gateway.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/xs1/
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from functools import partial
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_SSL, CONF_USERNAME)
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['xs1-api-client==2.3.5']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'xs1'
|
||||
ACTUATORS = 'actuators'
|
||||
SENSORS = 'sensors'
|
||||
|
||||
# define configuration parameters
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_PORT, default=80): cv.string,
|
||||
vol.Optional(CONF_SSL, default=False): cv.boolean,
|
||||
vol.Optional(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_PASSWORD): cv.string
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
XS1_COMPONENTS = [
|
||||
'switch',
|
||||
'sensor',
|
||||
'climate'
|
||||
]
|
||||
|
||||
# Lock used to limit the amount of concurrent update requests
|
||||
# as the XS1 Gateway can only handle a very
|
||||
# small amount of concurrent requests
|
||||
UPDATE_LOCK = asyncio.Lock()
|
||||
|
||||
|
||||
def _create_controller_api(host, port, ssl, user, password):
|
||||
"""Create an api instance to use for communication."""
|
||||
import xs1_api_client
|
||||
|
||||
try:
|
||||
return xs1_api_client.XS1(
|
||||
host=host,
|
||||
port=port,
|
||||
ssl=ssl,
|
||||
user=user,
|
||||
password=password)
|
||||
except ConnectionError as error:
|
||||
_LOGGER.error("Failed to create XS1 api client "
|
||||
"because of a connection error: %s", error)
|
||||
return None
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up XS1 Component."""
|
||||
_LOGGER.debug("Initializing XS1")
|
||||
|
||||
host = config[DOMAIN][CONF_HOST]
|
||||
port = config[DOMAIN][CONF_PORT]
|
||||
ssl = config[DOMAIN][CONF_SSL]
|
||||
user = config[DOMAIN].get(CONF_USERNAME)
|
||||
password = config[DOMAIN].get(CONF_PASSWORD)
|
||||
|
||||
# initialize XS1 API
|
||||
xs1 = await hass.async_add_executor_job(
|
||||
partial(_create_controller_api,
|
||||
host, port, ssl, user, password))
|
||||
if xs1 is None:
|
||||
return False
|
||||
|
||||
_LOGGER.debug(
|
||||
"Establishing connection to XS1 gateway and retrieving data...")
|
||||
|
||||
hass.data[DOMAIN] = {}
|
||||
|
||||
actuators = await hass.async_add_executor_job(
|
||||
partial(xs1.get_all_actuators, enabled=True))
|
||||
sensors = await hass.async_add_executor_job(
|
||||
partial(xs1.get_all_sensors, enabled=True))
|
||||
|
||||
hass.data[DOMAIN][ACTUATORS] = actuators
|
||||
hass.data[DOMAIN][SENSORS] = sensors
|
||||
|
||||
_LOGGER.debug("Loading components for XS1 platform...")
|
||||
# load components for supported devices
|
||||
for component in XS1_COMPONENTS:
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(
|
||||
hass, component, DOMAIN, {}, config))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class XS1DeviceEntity(Entity):
|
||||
"""Representation of a base XS1 device."""
|
||||
|
||||
def __init__(self, device):
|
||||
"""Initialize the XS1 device."""
|
||||
self.device = device
|
||||
|
||||
async def async_update(self):
|
||||
"""Retrieve latest device state."""
|
||||
async with UPDATE_LOCK:
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(self.device.update))
|
|
@ -0,0 +1,109 @@
|
|||
"""
|
||||
Support for XS1 climate devices.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/climate.xs1/
|
||||
"""
|
||||
from functools import partial
|
||||
import logging
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
ATTR_TEMPERATURE, ClimateDevice, SUPPORT_TARGET_TEMPERATURE)
|
||||
from homeassistant.components.xs1 import (
|
||||
ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity)
|
||||
|
||||
DEPENDENCIES = ['xs1']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MIN_TEMP = 8
|
||||
MAX_TEMP = 25
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the XS1 thermostat platform."""
|
||||
from xs1_api_client.api_constants import ActuatorType
|
||||
|
||||
actuators = hass.data[COMPONENT_DOMAIN][ACTUATORS]
|
||||
sensors = hass.data[COMPONENT_DOMAIN][SENSORS]
|
||||
|
||||
thermostat_entities = []
|
||||
for actuator in actuators:
|
||||
if actuator.type() == ActuatorType.TEMPERATURE:
|
||||
# Search for a matching sensor (by name)
|
||||
actuator_name = actuator.name()
|
||||
|
||||
matching_sensor = None
|
||||
for sensor in sensors:
|
||||
if actuator_name in sensor.name():
|
||||
matching_sensor = sensor
|
||||
|
||||
break
|
||||
|
||||
thermostat_entities.append(
|
||||
XS1ThermostatEntity(actuator, matching_sensor))
|
||||
|
||||
async_add_entities(thermostat_entities)
|
||||
|
||||
|
||||
class XS1ThermostatEntity(XS1DeviceEntity, ClimateDevice):
|
||||
"""Representation of a XS1 thermostat."""
|
||||
|
||||
def __init__(self, device, sensor):
|
||||
"""Initialize the actuator."""
|
||||
super().__init__(device)
|
||||
self.sensor = sensor
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device if any."""
|
||||
return self.device.name()
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
return SUPPORT_TARGET_TEMPERATURE
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
if self.sensor is None:
|
||||
return None
|
||||
|
||||
return self.sensor.value()
|
||||
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement used by the platform."""
|
||||
return self.device.unit()
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the current target temperature."""
|
||||
return self.device.new_value()
|
||||
|
||||
@property
|
||||
def min_temp(self):
|
||||
"""Return the minimum temperature."""
|
||||
return MIN_TEMP
|
||||
|
||||
@property
|
||||
def max_temp(self):
|
||||
"""Return the maximum temperature."""
|
||||
return MAX_TEMP
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
"""Set new target temperature."""
|
||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||
|
||||
self.device.set_value(temp)
|
||||
|
||||
if self.sensor is not None:
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
async def async_update(self):
|
||||
"""Also update the sensor when available."""
|
||||
await super().async_update()
|
||||
if self.sensor is not None:
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(self.sensor.update))
|
|
@ -0,0 +1,57 @@
|
|||
"""
|
||||
Support for XS1 sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.xs1/
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.xs1 import (
|
||||
ACTUATORS, DOMAIN as COMPONENT_DOMAIN, SENSORS, XS1DeviceEntity)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
DEPENDENCIES = ['xs1']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the XS1 sensor platform."""
|
||||
from xs1_api_client.api_constants import ActuatorType
|
||||
|
||||
sensors = hass.data[COMPONENT_DOMAIN][SENSORS]
|
||||
actuators = hass.data[COMPONENT_DOMAIN][ACTUATORS]
|
||||
|
||||
sensor_entities = []
|
||||
for sensor in sensors:
|
||||
belongs_to_climate_actuator = False
|
||||
for actuator in actuators:
|
||||
if actuator.type() == ActuatorType.TEMPERATURE and \
|
||||
actuator.name() in sensor.name():
|
||||
belongs_to_climate_actuator = True
|
||||
break
|
||||
|
||||
if not belongs_to_climate_actuator:
|
||||
sensor_entities.append(XS1Sensor(sensor))
|
||||
|
||||
async_add_entities(sensor_entities)
|
||||
|
||||
|
||||
class XS1Sensor(XS1DeviceEntity, Entity):
|
||||
"""Representation of a Sensor."""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self.device.name()
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self.device.value()
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement."""
|
||||
return self.device.unit()
|
|
@ -0,0 +1,52 @@
|
|||
"""
|
||||
Support for XS1 switches.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/switch.xs1/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.xs1 import (
|
||||
ACTUATORS, DOMAIN as COMPONENT_DOMAIN, XS1DeviceEntity)
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
|
||||
DEPENDENCIES = ['xs1']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the XS1 switch platform."""
|
||||
from xs1_api_client.api_constants import ActuatorType
|
||||
|
||||
actuators = hass.data[COMPONENT_DOMAIN][ACTUATORS]
|
||||
|
||||
switch_entities = []
|
||||
for actuator in actuators:
|
||||
if (actuator.type() == ActuatorType.SWITCH) or \
|
||||
(actuator.type() == ActuatorType.DIMMER):
|
||||
switch_entities.append(XS1SwitchEntity(actuator))
|
||||
|
||||
async_add_entities(switch_entities)
|
||||
|
||||
|
||||
class XS1SwitchEntity(XS1DeviceEntity, ToggleEntity):
|
||||
"""Representation of a XS1 switch actuator."""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device if any."""
|
||||
return self.device.name()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if switch is on."""
|
||||
return self.device.value() == 100
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the device on."""
|
||||
self.device.turn_on()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the device off."""
|
||||
self.device.turn_off()
|
|
@ -1754,6 +1754,9 @@ xknx==0.9.3
|
|||
# homeassistant.components.sensor.zestimate
|
||||
xmltodict==0.11.0
|
||||
|
||||
# homeassistant.components.xs1
|
||||
xs1-api-client==2.3.5
|
||||
|
||||
# homeassistant.components.sensor.yweather
|
||||
# homeassistant.components.weather.yweather
|
||||
yahooweather==0.10
|
||||
|
|
Loading…
Reference in New Issue