mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 06:07:17 +00:00
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 package
This commit is contained in:
parent
a9672b0d52
commit
542f024356
@ -655,6 +655,7 @@ omit =
|
|||||||
homeassistant/components/wirelesstag/*
|
homeassistant/components/wirelesstag/*
|
||||||
homeassistant/components/xiaomi_aqara/*
|
homeassistant/components/xiaomi_aqara/*
|
||||||
homeassistant/components/xiaomi_miio/*
|
homeassistant/components/xiaomi_miio/*
|
||||||
|
homeassistant/components/xs1/*
|
||||||
homeassistant/components/zabbix/*
|
homeassistant/components/zabbix/*
|
||||||
homeassistant/components/zeroconf/*
|
homeassistant/components/zeroconf/*
|
||||||
homeassistant/components/zha/__init__.py
|
homeassistant/components/zha/__init__.py
|
||||||
|
119
homeassistant/components/xs1/__init__.py
Normal file
119
homeassistant/components/xs1/__init__.py
Normal file
@ -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))
|
109
homeassistant/components/xs1/climate.py
Normal file
109
homeassistant/components/xs1/climate.py
Normal file
@ -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))
|
57
homeassistant/components/xs1/sensor.py
Normal file
57
homeassistant/components/xs1/sensor.py
Normal file
@ -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()
|
52
homeassistant/components/xs1/switch.py
Normal file
52
homeassistant/components/xs1/switch.py
Normal file
@ -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
|
# homeassistant.components.sensor.zestimate
|
||||||
xmltodict==0.11.0
|
xmltodict==0.11.0
|
||||||
|
|
||||||
|
# homeassistant.components.xs1
|
||||||
|
xs1-api-client==2.3.5
|
||||||
|
|
||||||
# homeassistant.components.sensor.yweather
|
# homeassistant.components.sensor.yweather
|
||||||
# homeassistant.components.weather.yweather
|
# homeassistant.components.weather.yweather
|
||||||
yahooweather==0.10
|
yahooweather==0.10
|
||||||
|
Loading…
x
Reference in New Issue
Block a user