diff --git a/homeassistant/components/lcn/__init__.py b/homeassistant/components/lcn/__init__.py index faba23a52b9..fa18c0e5784 100644 --- a/homeassistant/components/lcn/__init__.py +++ b/homeassistant/components/lcn/__init__.py @@ -2,11 +2,8 @@ import logging import pypck -import voluptuous as vol -from homeassistant.components.climate import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP from homeassistant.const import ( - CONF_ADDRESS, CONF_BINARY_SENSORS, CONF_COVERS, CONF_HOST, @@ -16,52 +13,21 @@ from homeassistant.const import ( CONF_PORT, CONF_SENSORS, CONF_SWITCHES, - CONF_UNIT_OF_MEASUREMENT, CONF_USERNAME, - TEMP_CELSIUS, - TEMP_FAHRENHEIT, ) -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.entity import Entity from .const import ( - BINSENSOR_PORTS, CONF_CLIMATES, CONF_CONNECTIONS, CONF_DIM_MODE, - CONF_DIMMABLE, - CONF_LOCKABLE, - CONF_MAX_TEMP, - CONF_MIN_TEMP, - CONF_MOTOR, - CONF_OUTPUT, - CONF_OUTPUTS, - CONF_REGISTER, - CONF_REVERSE_TIME, - CONF_SCENE, CONF_SCENES, - CONF_SETPOINT, CONF_SK_NUM_TRIES, - CONF_SOURCE, - CONF_TRANSITION, DATA_LCN, - DIM_MODES, DOMAIN, - KEYS, - LED_PORTS, - LOGICOP_PORTS, - MOTOR_PORTS, - MOTOR_REVERSE_TIME, - OUTPUT_PORTS, - RELAY_PORTS, - S0_INPUTS, - SETPOINTS, - THRESHOLDS, - VAR_UNITS, - VARIABLES, ) -from .helpers import has_unique_connection_names, is_address +from .schemas import CONFIG_SCHEMA # noqa: 401 from .services import ( DynText, Led, @@ -80,141 +46,6 @@ from .services import ( _LOGGER = logging.getLogger(__name__) -BINARY_SENSORS_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ADDRESS): is_address, - vol.Required(CONF_SOURCE): vol.All( - vol.Upper, vol.In(SETPOINTS + KEYS + BINSENSOR_PORTS) - ), - } -) - -CLIMATES_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ADDRESS): is_address, - vol.Required(CONF_SOURCE): vol.All(vol.Upper, vol.In(VARIABLES)), - vol.Required(CONF_SETPOINT): vol.All(vol.Upper, vol.In(VARIABLES + SETPOINTS)), - vol.Optional(CONF_MAX_TEMP, default=DEFAULT_MAX_TEMP): vol.Coerce(float), - vol.Optional(CONF_MIN_TEMP, default=DEFAULT_MIN_TEMP): vol.Coerce(float), - vol.Optional(CONF_LOCKABLE, default=False): vol.Coerce(bool), - vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=TEMP_CELSIUS): vol.In( - TEMP_CELSIUS, TEMP_FAHRENHEIT - ), - } -) - -COVERS_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ADDRESS): is_address, - vol.Required(CONF_MOTOR): vol.All(vol.Upper, vol.In(MOTOR_PORTS)), - vol.Optional(CONF_REVERSE_TIME): vol.All(vol.Upper, vol.In(MOTOR_REVERSE_TIME)), - } -) - -LIGHTS_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ADDRESS): is_address, - vol.Required(CONF_OUTPUT): vol.All( - vol.Upper, vol.In(OUTPUT_PORTS + RELAY_PORTS) - ), - vol.Optional(CONF_DIMMABLE, default=False): vol.Coerce(bool), - vol.Optional(CONF_TRANSITION, default=0): vol.All( - vol.Coerce(float), vol.Range(min=0.0, max=486.0), lambda value: value * 1000 - ), - } -) - -SCENES_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ADDRESS): is_address, - vol.Required(CONF_REGISTER): vol.All(vol.Coerce(int), vol.Range(0, 9)), - vol.Required(CONF_SCENE): vol.All(vol.Coerce(int), vol.Range(0, 9)), - vol.Optional(CONF_OUTPUTS): vol.All( - cv.ensure_list, [vol.All(vol.Upper, vol.In(OUTPUT_PORTS + RELAY_PORTS))] - ), - vol.Optional(CONF_TRANSITION, default=None): vol.Any( - vol.All( - vol.Coerce(int), - vol.Range(min=0.0, max=486.0), - lambda value: value * 1000, - ), - None, - ), - } -) - -SENSORS_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ADDRESS): is_address, - vol.Required(CONF_SOURCE): vol.All( - vol.Upper, - vol.In( - VARIABLES - + SETPOINTS - + THRESHOLDS - + S0_INPUTS - + LED_PORTS - + LOGICOP_PORTS - ), - ), - vol.Optional(CONF_UNIT_OF_MEASUREMENT, default="native"): vol.All( - vol.Upper, vol.In(VAR_UNITS) - ), - } -) - -SWITCHES_SCHEMA = vol.Schema( - { - vol.Required(CONF_NAME): cv.string, - vol.Required(CONF_ADDRESS): is_address, - vol.Required(CONF_OUTPUT): vol.All( - vol.Upper, vol.In(OUTPUT_PORTS + RELAY_PORTS) - ), - } -) - -CONNECTION_SCHEMA = vol.Schema( - { - vol.Required(CONF_HOST): cv.string, - vol.Required(CONF_PORT): cv.port, - vol.Required(CONF_USERNAME): cv.string, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_SK_NUM_TRIES, default=0): cv.positive_int, - vol.Optional(CONF_DIM_MODE, default="steps50"): vol.All( - vol.Upper, vol.In(DIM_MODES) - ), - vol.Optional(CONF_NAME): cv.string, - } -) - -CONFIG_SCHEMA = vol.Schema( - { - DOMAIN: vol.Schema( - { - vol.Required(CONF_CONNECTIONS): vol.All( - cv.ensure_list, has_unique_connection_names, [CONNECTION_SCHEMA] - ), - vol.Optional(CONF_BINARY_SENSORS): vol.All( - cv.ensure_list, [BINARY_SENSORS_SCHEMA] - ), - vol.Optional(CONF_CLIMATES): vol.All(cv.ensure_list, [CLIMATES_SCHEMA]), - vol.Optional(CONF_COVERS): vol.All(cv.ensure_list, [COVERS_SCHEMA]), - vol.Optional(CONF_LIGHTS): vol.All(cv.ensure_list, [LIGHTS_SCHEMA]), - vol.Optional(CONF_SCENES): vol.All(cv.ensure_list, [SCENES_SCHEMA]), - vol.Optional(CONF_SENSORS): vol.All(cv.ensure_list, [SENSORS_SCHEMA]), - vol.Optional(CONF_SWITCHES): vol.All(cv.ensure_list, [SWITCHES_SCHEMA]), - } - ) - }, - extra=vol.ALLOW_EXTRA, -) - async def async_setup(hass, config): """Set up the LCN component.""" @@ -292,13 +123,13 @@ async def async_setup(hass, config): return True -class LcnDevice(Entity): +class LcnEntity(Entity): """Parent class for all devices associated with the LCN component.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN device.""" self.config = config - self.address_connection = address_connection + self.device_connection = device_connection self._name = config[CONF_NAME] @property @@ -308,7 +139,7 @@ class LcnDevice(Entity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" - self.address_connection.register_for_inputs(self.input_received) + self.device_connection.register_for_inputs(self.input_received) @property def name(self): @@ -317,4 +148,3 @@ class LcnDevice(Entity): def input_received(self, input_obj): """Set state/value when LCN input object (command) is received.""" - raise NotImplementedError("Pure virtual function.") diff --git a/homeassistant/components/lcn/binary_sensor.py b/homeassistant/components/lcn/binary_sensor.py index 7b4cedfebad..5d712045c93 100644 --- a/homeassistant/components/lcn/binary_sensor.py +++ b/homeassistant/components/lcn/binary_sensor.py @@ -4,7 +4,7 @@ import pypck from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.const import CONF_ADDRESS -from . import LcnDevice +from . import LcnEntity from .const import BINSENSOR_PORTS, CONF_CONNECTIONS, CONF_SOURCE, DATA_LCN, SETPOINTS from .helpers import get_connection @@ -36,12 +36,12 @@ async def async_setup_platform( async_add_entities(devices) -class LcnRegulatorLockSensor(LcnDevice, BinarySensorEntity): +class LcnRegulatorLockSensor(LcnEntity, BinarySensorEntity): """Representation of a LCN binary sensor for regulator locks.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN binary sensor.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.setpoint_variable = pypck.lcn_defs.Var[config[CONF_SOURCE]] @@ -50,7 +50,7 @@ class LcnRegulatorLockSensor(LcnDevice, BinarySensorEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler( + await self.device_connection.activate_status_request_handler( self.setpoint_variable ) @@ -71,12 +71,12 @@ class LcnRegulatorLockSensor(LcnDevice, BinarySensorEntity): self.async_write_ha_state() -class LcnBinarySensor(LcnDevice, BinarySensorEntity): +class LcnBinarySensor(LcnEntity, BinarySensorEntity): """Representation of a LCN binary sensor for binary sensor ports.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN binary sensor.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.bin_sensor_port = pypck.lcn_defs.BinSensorPort[config[CONF_SOURCE]] @@ -85,7 +85,7 @@ class LcnBinarySensor(LcnDevice, BinarySensorEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler( + await self.device_connection.activate_status_request_handler( self.bin_sensor_port ) @@ -103,12 +103,12 @@ class LcnBinarySensor(LcnDevice, BinarySensorEntity): self.async_write_ha_state() -class LcnLockKeysSensor(LcnDevice, BinarySensorEntity): +class LcnLockKeysSensor(LcnEntity, BinarySensorEntity): """Representation of a LCN sensor for key locks.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN sensor.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.source = pypck.lcn_defs.Key[config[CONF_SOURCE]] self._value = None @@ -116,7 +116,7 @@ class LcnLockKeysSensor(LcnDevice, BinarySensorEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.source) + await self.device_connection.activate_status_request_handler(self.source) @property def is_on(self): diff --git a/homeassistant/components/lcn/climate.py b/homeassistant/components/lcn/climate.py index 8b0f4951bf9..e3eb92a426f 100644 --- a/homeassistant/components/lcn/climate.py +++ b/homeassistant/components/lcn/climate.py @@ -5,7 +5,7 @@ import pypck from homeassistant.components.climate import ClimateEntity, const from homeassistant.const import ATTR_TEMPERATURE, CONF_ADDRESS, CONF_UNIT_OF_MEASUREMENT -from . import LcnDevice +from . import LcnEntity from .const import ( CONF_CONNECTIONS, CONF_LOCKABLE, @@ -40,12 +40,12 @@ async def async_setup_platform( async_add_entities(devices) -class LcnClimate(LcnDevice, ClimateEntity): +class LcnClimate(LcnEntity, ClimateEntity): """Representation of a LCN climate device.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize of a LCN climate device.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.variable = pypck.lcn_defs.Var[config[CONF_SOURCE]] self.setpoint = pypck.lcn_defs.Var[config[CONF_SETPOINT]] @@ -63,8 +63,8 @@ class LcnClimate(LcnDevice, ClimateEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.variable) - await self.address_connection.activate_status_request_handler(self.setpoint) + await self.device_connection.activate_status_request_handler(self.variable) + await self.device_connection.activate_status_request_handler(self.setpoint) @property def supported_features(self): @@ -120,16 +120,14 @@ class LcnClimate(LcnDevice, ClimateEntity): async def async_set_hvac_mode(self, hvac_mode): """Set new target hvac mode.""" if hvac_mode == const.HVAC_MODE_HEAT: - if not await self.address_connection.lock_regulator( + if not await self.device_connection.lock_regulator( self.regulator_id, False ): return self._is_on = True self.async_write_ha_state() elif hvac_mode == const.HVAC_MODE_OFF: - if not await self.address_connection.lock_regulator( - self.regulator_id, True - ): + if not await self.device_connection.lock_regulator(self.regulator_id, True): return self._is_on = False self._target_temperature = None @@ -141,7 +139,7 @@ class LcnClimate(LcnDevice, ClimateEntity): if temperature is None: return - if not await self.address_connection.var_abs( + if not await self.device_connection.var_abs( self.setpoint, temperature, self.unit ): return diff --git a/homeassistant/components/lcn/cover.py b/homeassistant/components/lcn/cover.py index ae88441f89e..c5e407573ba 100644 --- a/homeassistant/components/lcn/cover.py +++ b/homeassistant/components/lcn/cover.py @@ -4,7 +4,7 @@ import pypck from homeassistant.components.cover import CoverEntity from homeassistant.const import CONF_ADDRESS -from . import LcnDevice +from . import LcnEntity from .const import CONF_CONNECTIONS, CONF_MOTOR, CONF_REVERSE_TIME, DATA_LCN from .helpers import get_connection @@ -34,12 +34,12 @@ async def async_setup_platform( async_add_entities(devices) -class LcnOutputsCover(LcnDevice, CoverEntity): +class LcnOutputsCover(LcnEntity, CoverEntity): """Representation of a LCN cover connected to output ports.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN cover.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.output_ids = [ pypck.lcn_defs.OutputPort["OUTPUTUP"].value, @@ -59,10 +59,10 @@ class LcnOutputsCover(LcnDevice, CoverEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler( + await self.device_connection.activate_status_request_handler( pypck.lcn_defs.OutputPort["OUTPUTUP"] ) - await self.address_connection.activate_status_request_handler( + await self.device_connection.activate_status_request_handler( pypck.lcn_defs.OutputPort["OUTPUTDOWN"] ) @@ -89,7 +89,7 @@ class LcnOutputsCover(LcnDevice, CoverEntity): async def async_close_cover(self, **kwargs): """Close the cover.""" state = pypck.lcn_defs.MotorStateModifier.DOWN - if not await self.address_connection.control_motors_outputs( + if not await self.device_connection.control_motors_outputs( state, self.reverse_time ): return @@ -100,7 +100,7 @@ class LcnOutputsCover(LcnDevice, CoverEntity): async def async_open_cover(self, **kwargs): """Open the cover.""" state = pypck.lcn_defs.MotorStateModifier.UP - if not await self.address_connection.control_motors_outputs( + if not await self.device_connection.control_motors_outputs( state, self.reverse_time ): return @@ -112,7 +112,7 @@ class LcnOutputsCover(LcnDevice, CoverEntity): async def async_stop_cover(self, **kwargs): """Stop the cover.""" state = pypck.lcn_defs.MotorStateModifier.STOP - if not await self.address_connection.control_motors_outputs(state): + if not await self.device_connection.control_motors_outputs(state): return self._is_closing = False self._is_opening = False @@ -143,12 +143,12 @@ class LcnOutputsCover(LcnDevice, CoverEntity): self.async_write_ha_state() -class LcnRelayCover(LcnDevice, CoverEntity): +class LcnRelayCover(LcnEntity, CoverEntity): """Representation of a LCN cover connected to relays.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN cover.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.motor = pypck.lcn_defs.MotorPort[config[CONF_MOTOR]] self.motor_port_onoff = self.motor.value * 2 @@ -161,7 +161,7 @@ class LcnRelayCover(LcnDevice, CoverEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.motor) + await self.device_connection.activate_status_request_handler(self.motor) @property def is_closed(self): @@ -187,7 +187,7 @@ class LcnRelayCover(LcnDevice, CoverEntity): """Close the cover.""" states = [pypck.lcn_defs.MotorStateModifier.NOCHANGE] * 4 states[self.motor.value] = pypck.lcn_defs.MotorStateModifier.DOWN - if not await self.address_connection.control_motors_relays(states): + if not await self.device_connection.control_motors_relays(states): return self._is_opening = False self._is_closing = True @@ -197,7 +197,7 @@ class LcnRelayCover(LcnDevice, CoverEntity): """Open the cover.""" states = [pypck.lcn_defs.MotorStateModifier.NOCHANGE] * 4 states[self.motor.value] = pypck.lcn_defs.MotorStateModifier.UP - if not await self.address_connection.control_motors_relays(states): + if not await self.device_connection.control_motors_relays(states): return self._is_closed = False self._is_opening = True @@ -208,7 +208,7 @@ class LcnRelayCover(LcnDevice, CoverEntity): """Stop the cover.""" states = [pypck.lcn_defs.MotorStateModifier.NOCHANGE] * 4 states[self.motor.value] = pypck.lcn_defs.MotorStateModifier.STOP - if not await self.address_connection.control_motors_relays(states): + if not await self.device_connection.control_motors_relays(states): return self._is_closing = False self._is_opening = False diff --git a/homeassistant/components/lcn/helpers.py b/homeassistant/components/lcn/helpers.py index f4545817c9f..18342aa1d98 100644 --- a/homeassistant/components/lcn/helpers.py +++ b/homeassistant/components/lcn/helpers.py @@ -26,23 +26,25 @@ def get_connection(connections, connection_id=None): return connection -def has_unique_connection_names(connections): +def has_unique_host_names(hosts): """Validate that all connection names are unique. Use 'pchk' as default connection_name (or add a numeric suffix if pchk' is already in use. """ - for suffix, connection in enumerate(connections): - connection_name = connection.get(CONF_NAME) - if connection_name is None: + suffix = 0 + for host in hosts: + host_name = host.get(CONF_NAME) + if host_name is None: if suffix == 0: - connection[CONF_NAME] = DEFAULT_NAME + host[CONF_NAME] = DEFAULT_NAME else: - connection[CONF_NAME] = f"{DEFAULT_NAME}{suffix:d}" + host[CONF_NAME] = f"{DEFAULT_NAME}{suffix:d}" + suffix += 1 schema = vol.Schema(vol.Unique()) - schema([connection.get(CONF_NAME) for connection in connections]) - return connections + schema([host.get(CONF_NAME) for host in hosts]) + return hosts def is_address(value): diff --git a/homeassistant/components/lcn/light.py b/homeassistant/components/lcn/light.py index def025e0cf2..c6ef895b7df 100644 --- a/homeassistant/components/lcn/light.py +++ b/homeassistant/components/lcn/light.py @@ -10,7 +10,7 @@ from homeassistant.components.light import ( ) from homeassistant.const import CONF_ADDRESS -from . import LcnDevice +from . import LcnEntity from .const import ( CONF_CONNECTIONS, CONF_DIMMABLE, @@ -49,12 +49,12 @@ async def async_setup_platform( async_add_entities(devices) -class LcnOutputLight(LcnDevice, LightEntity): +class LcnOutputLight(LcnEntity, LightEntity): """Representation of a LCN light for output ports.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN light.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.output = pypck.lcn_defs.OutputPort[config[CONF_OUTPUT]] @@ -68,7 +68,7 @@ class LcnOutputLight(LcnDevice, LightEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.output) + await self.device_connection.activate_status_request_handler(self.output) @property def supported_features(self): @@ -100,7 +100,7 @@ class LcnOutputLight(LcnDevice, LightEntity): else: transition = self._transition - if not await self.address_connection.dim_output( + if not await self.device_connection.dim_output( self.output.value, percent, transition ): return @@ -117,7 +117,7 @@ class LcnOutputLight(LcnDevice, LightEntity): else: transition = self._transition - if not await self.address_connection.dim_output( + if not await self.device_connection.dim_output( self.output.value, 0, transition ): return @@ -141,12 +141,12 @@ class LcnOutputLight(LcnDevice, LightEntity): self.async_write_ha_state() -class LcnRelayLight(LcnDevice, LightEntity): +class LcnRelayLight(LcnEntity, LightEntity): """Representation of a LCN light for relay ports.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN light.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.output = pypck.lcn_defs.RelayPort[config[CONF_OUTPUT]] @@ -155,7 +155,7 @@ class LcnRelayLight(LcnDevice, LightEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.output) + await self.device_connection.activate_status_request_handler(self.output) @property def is_on(self): @@ -167,7 +167,7 @@ class LcnRelayLight(LcnDevice, LightEntity): states = [pypck.lcn_defs.RelayStateModifier.NOCHANGE] * 8 states[self.output.value] = pypck.lcn_defs.RelayStateModifier.ON - if not await self.address_connection.control_relays(states): + if not await self.device_connection.control_relays(states): return self._is_on = True self.async_write_ha_state() @@ -177,7 +177,7 @@ class LcnRelayLight(LcnDevice, LightEntity): states = [pypck.lcn_defs.RelayStateModifier.NOCHANGE] * 8 states[self.output.value] = pypck.lcn_defs.RelayStateModifier.OFF - if not await self.address_connection.control_relays(states): + if not await self.device_connection.control_relays(states): return self._is_on = False self.async_write_ha_state() diff --git a/homeassistant/components/lcn/scene.py b/homeassistant/components/lcn/scene.py index cac13ee1653..ed211473e29 100644 --- a/homeassistant/components/lcn/scene.py +++ b/homeassistant/components/lcn/scene.py @@ -6,7 +6,7 @@ import pypck from homeassistant.components.scene import Scene from homeassistant.const import CONF_ADDRESS -from . import LcnDevice +from . import LcnEntity from .const import ( CONF_CONNECTIONS, CONF_OUTPUTS, @@ -41,12 +41,12 @@ async def async_setup_platform( async_add_entities(devices) -class LcnScene(LcnDevice, Scene): +class LcnScene(LcnEntity, Scene): """Representation of a LCN scene.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN scene.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.register_id = config[CONF_REGISTER] self.scene_id = config[CONF_SCENE] @@ -69,7 +69,7 @@ class LcnScene(LcnDevice, Scene): async def async_activate(self, **kwargs: Any) -> None: """Activate scene.""" - await self.address_connection.activate_scene( + await self.device_connection.activate_scene( self.register_id, self.scene_id, self.output_ports, diff --git a/homeassistant/components/lcn/schemas.py b/homeassistant/components/lcn/schemas.py new file mode 100644 index 00000000000..1cc51f400da --- /dev/null +++ b/homeassistant/components/lcn/schemas.py @@ -0,0 +1,190 @@ +"""Schema definitions for LCN configuration and websockets api.""" +import voluptuous as vol + +from homeassistant.components.climate import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP +from homeassistant.const import ( + CONF_ADDRESS, + CONF_BINARY_SENSORS, + CONF_COVERS, + CONF_HOST, + CONF_LIGHTS, + CONF_NAME, + CONF_PASSWORD, + CONF_PORT, + CONF_SENSORS, + CONF_SWITCHES, + CONF_UNIT_OF_MEASUREMENT, + CONF_USERNAME, +) +import homeassistant.helpers.config_validation as cv + +from .const import ( + BINSENSOR_PORTS, + CONF_CLIMATES, + CONF_CONNECTIONS, + CONF_DIM_MODE, + CONF_DIMMABLE, + CONF_LOCKABLE, + CONF_MAX_TEMP, + CONF_MIN_TEMP, + CONF_MOTOR, + CONF_OUTPUT, + CONF_OUTPUTS, + CONF_REGISTER, + CONF_REVERSE_TIME, + CONF_SCENE, + CONF_SCENES, + CONF_SETPOINT, + CONF_SK_NUM_TRIES, + CONF_SOURCE, + CONF_TRANSITION, + DIM_MODES, + DOMAIN, + KEYS, + LED_PORTS, + LOGICOP_PORTS, + MOTOR_PORTS, + MOTOR_REVERSE_TIME, + OUTPUT_PORTS, + RELAY_PORTS, + S0_INPUTS, + SETPOINTS, + TEMP_CELSIUS, + TEMP_FAHRENHEIT, + THRESHOLDS, + VAR_UNITS, + VARIABLES, +) +from .helpers import has_unique_host_names, is_address + +# +# Domain data +# + +DOMAIN_DATA_BINARY_SENSOR = { + vol.Required(CONF_SOURCE): vol.All( + vol.Upper, vol.In(SETPOINTS + KEYS + BINSENSOR_PORTS) + ), +} + + +DOMAIN_DATA_CLIMATE = { + vol.Required(CONF_SOURCE): vol.All(vol.Upper, vol.In(VARIABLES)), + vol.Required(CONF_SETPOINT): vol.All(vol.Upper, vol.In(VARIABLES + SETPOINTS)), + vol.Optional(CONF_MAX_TEMP, default=DEFAULT_MAX_TEMP): vol.Coerce(float), + vol.Optional(CONF_MIN_TEMP, default=DEFAULT_MIN_TEMP): vol.Coerce(float), + vol.Optional(CONF_LOCKABLE, default=False): vol.Coerce(bool), + vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=TEMP_CELSIUS): vol.In( + TEMP_CELSIUS, TEMP_FAHRENHEIT + ), +} + + +DOMAIN_DATA_COVER = { + vol.Required(CONF_MOTOR): vol.All(vol.Upper, vol.In(MOTOR_PORTS)), + vol.Optional(CONF_REVERSE_TIME, default="rt1200"): vol.All( + vol.Upper, vol.In(MOTOR_REVERSE_TIME) + ), +} + + +DOMAIN_DATA_LIGHT = { + vol.Required(CONF_OUTPUT): vol.All(vol.Upper, vol.In(OUTPUT_PORTS + RELAY_PORTS)), + vol.Optional(CONF_DIMMABLE, default=False): vol.Coerce(bool), + vol.Optional(CONF_TRANSITION, default=0): vol.All( + vol.Coerce(float), vol.Range(min=0.0, max=486.0), lambda value: value * 1000 + ), +} + + +DOMAIN_DATA_SCENE = { + vol.Required(CONF_REGISTER): vol.All(vol.Coerce(int), vol.Range(0, 9)), + vol.Required(CONF_SCENE): vol.All(vol.Coerce(int), vol.Range(0, 9)), + vol.Optional(CONF_OUTPUTS, default=[]): vol.All( + cv.ensure_list, [vol.All(vol.Upper, vol.In(OUTPUT_PORTS + RELAY_PORTS))] + ), + vol.Optional(CONF_TRANSITION, default=None): vol.Any( + vol.All( + vol.Coerce(int), + vol.Range(min=0.0, max=486.0), + lambda value: value * 1000, + ), + None, + ), +} + +DOMAIN_DATA_SENSOR = { + vol.Required(CONF_SOURCE): vol.All( + vol.Upper, + vol.In( + VARIABLES + SETPOINTS + THRESHOLDS + S0_INPUTS + LED_PORTS + LOGICOP_PORTS + ), + ), + vol.Optional(CONF_UNIT_OF_MEASUREMENT, default="native"): vol.All( + vol.Upper, vol.In(VAR_UNITS) + ), +} + + +DOMAIN_DATA_SWITCH = { + vol.Required(CONF_OUTPUT): vol.All(vol.Upper, vol.In(OUTPUT_PORTS + RELAY_PORTS)), +} + +# +# Configuration +# + +DOMAIN_DATA_BASE = { + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_ADDRESS): is_address, +} + +BINARY_SENSORS_SCHEMA = vol.Schema({**DOMAIN_DATA_BASE, **DOMAIN_DATA_BINARY_SENSOR}) + +CLIMATES_SCHEMA = vol.Schema({**DOMAIN_DATA_BASE, **DOMAIN_DATA_CLIMATE}) + +COVERS_SCHEMA = vol.Schema({**DOMAIN_DATA_BASE, **DOMAIN_DATA_COVER}) + +LIGHTS_SCHEMA = vol.Schema({**DOMAIN_DATA_BASE, **DOMAIN_DATA_LIGHT}) + +SCENES_SCHEMA = vol.Schema({**DOMAIN_DATA_BASE, **DOMAIN_DATA_SCENE}) + +SENSORS_SCHEMA = vol.Schema({**DOMAIN_DATA_BASE, **DOMAIN_DATA_SENSOR}) + +SWITCHES_SCHEMA = vol.Schema({**DOMAIN_DATA_BASE, **DOMAIN_DATA_SWITCH}) + +CONNECTION_SCHEMA = vol.Schema( + { + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_PORT): cv.port, + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_SK_NUM_TRIES, default=0): cv.positive_int, + vol.Optional(CONF_DIM_MODE, default="steps50"): vol.All( + vol.Upper, vol.In(DIM_MODES) + ), + vol.Optional(CONF_NAME): cv.string, + } +) + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + vol.Required(CONF_CONNECTIONS): vol.All( + cv.ensure_list, has_unique_host_names, [CONNECTION_SCHEMA] + ), + vol.Optional(CONF_BINARY_SENSORS): vol.All( + cv.ensure_list, [BINARY_SENSORS_SCHEMA] + ), + vol.Optional(CONF_CLIMATES): vol.All(cv.ensure_list, [CLIMATES_SCHEMA]), + vol.Optional(CONF_COVERS): vol.All(cv.ensure_list, [COVERS_SCHEMA]), + vol.Optional(CONF_LIGHTS): vol.All(cv.ensure_list, [LIGHTS_SCHEMA]), + vol.Optional(CONF_SCENES): vol.All(cv.ensure_list, [SCENES_SCHEMA]), + vol.Optional(CONF_SENSORS): vol.All(cv.ensure_list, [SENSORS_SCHEMA]), + vol.Optional(CONF_SWITCHES): vol.All(cv.ensure_list, [SWITCHES_SCHEMA]), + } + ) + }, + extra=vol.ALLOW_EXTRA, +) diff --git a/homeassistant/components/lcn/sensor.py b/homeassistant/components/lcn/sensor.py index ddf7e61a3f6..26b54def974 100644 --- a/homeassistant/components/lcn/sensor.py +++ b/homeassistant/components/lcn/sensor.py @@ -3,7 +3,7 @@ import pypck from homeassistant.const import CONF_ADDRESS, CONF_UNIT_OF_MEASUREMENT -from . import LcnDevice +from . import LcnEntity from .const import ( CONF_CONNECTIONS, CONF_SOURCE, @@ -30,24 +30,24 @@ async def async_setup_platform( addr = pypck.lcn_addr.LcnAddr(*address) connections = hass.data[DATA_LCN][CONF_CONNECTIONS] connection = get_connection(connections, connection_id) - address_connection = connection.get_address_conn(addr) + device_connection = connection.get_address_conn(addr) if config[CONF_SOURCE] in VARIABLES + SETPOINTS + THRESHOLDS + S0_INPUTS: - device = LcnVariableSensor(config, address_connection) + device = LcnVariableSensor(config, device_connection) else: # in LED_PORTS + LOGICOP_PORTS - device = LcnLedLogicSensor(config, address_connection) + device = LcnLedLogicSensor(config, device_connection) devices.append(device) async_add_entities(devices) -class LcnVariableSensor(LcnDevice): +class LcnVariableSensor(LcnEntity): """Representation of a LCN sensor for variables.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN sensor.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.variable = pypck.lcn_defs.Var[config[CONF_SOURCE]] self.unit = pypck.lcn_defs.VarUnit.parse(config[CONF_UNIT_OF_MEASUREMENT]) @@ -57,7 +57,7 @@ class LcnVariableSensor(LcnDevice): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.variable) + await self.device_connection.activate_status_request_handler(self.variable) @property def state(self): @@ -81,12 +81,12 @@ class LcnVariableSensor(LcnDevice): self.async_write_ha_state() -class LcnLedLogicSensor(LcnDevice): +class LcnLedLogicSensor(LcnEntity): """Representation of a LCN sensor for leds and logicops.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN sensor.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) if config[CONF_SOURCE] in LED_PORTS: self.source = pypck.lcn_defs.LedPort[config[CONF_SOURCE]] @@ -98,7 +98,7 @@ class LcnLedLogicSensor(LcnDevice): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.source) + await self.device_connection.activate_status_request_handler(self.source) @property def state(self): diff --git a/homeassistant/components/lcn/switch.py b/homeassistant/components/lcn/switch.py index 1d6f7cb6df4..5891629627e 100644 --- a/homeassistant/components/lcn/switch.py +++ b/homeassistant/components/lcn/switch.py @@ -4,7 +4,7 @@ import pypck from homeassistant.components.switch import SwitchEntity from homeassistant.const import CONF_ADDRESS -from . import LcnDevice +from . import LcnEntity from .const import CONF_CONNECTIONS, CONF_OUTPUT, DATA_LCN, OUTPUT_PORTS from .helpers import get_connection @@ -36,12 +36,12 @@ async def async_setup_platform( async_add_entities(devices) -class LcnOutputSwitch(LcnDevice, SwitchEntity): +class LcnOutputSwitch(LcnEntity, SwitchEntity): """Representation of a LCN switch for output ports.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN switch.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.output = pypck.lcn_defs.OutputPort[config[CONF_OUTPUT]] @@ -50,7 +50,7 @@ class LcnOutputSwitch(LcnDevice, SwitchEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.output) + await self.device_connection.activate_status_request_handler(self.output) @property def is_on(self): @@ -59,14 +59,14 @@ class LcnOutputSwitch(LcnDevice, SwitchEntity): async def async_turn_on(self, **kwargs): """Turn the entity on.""" - if not await self.address_connection.dim_output(self.output.value, 100, 0): + if not await self.device_connection.dim_output(self.output.value, 100, 0): return self._is_on = True self.async_write_ha_state() async def async_turn_off(self, **kwargs): """Turn the entity off.""" - if not await self.address_connection.dim_output(self.output.value, 0, 0): + if not await self.device_connection.dim_output(self.output.value, 0, 0): return self._is_on = False self.async_write_ha_state() @@ -83,12 +83,12 @@ class LcnOutputSwitch(LcnDevice, SwitchEntity): self.async_write_ha_state() -class LcnRelaySwitch(LcnDevice, SwitchEntity): +class LcnRelaySwitch(LcnEntity, SwitchEntity): """Representation of a LCN switch for relay ports.""" - def __init__(self, config, address_connection): + def __init__(self, config, device_connection): """Initialize the LCN switch.""" - super().__init__(config, address_connection) + super().__init__(config, device_connection) self.output = pypck.lcn_defs.RelayPort[config[CONF_OUTPUT]] @@ -97,7 +97,7 @@ class LcnRelaySwitch(LcnDevice, SwitchEntity): async def async_added_to_hass(self): """Run when entity about to be added to hass.""" await super().async_added_to_hass() - await self.address_connection.activate_status_request_handler(self.output) + await self.device_connection.activate_status_request_handler(self.output) @property def is_on(self): @@ -108,7 +108,7 @@ class LcnRelaySwitch(LcnDevice, SwitchEntity): """Turn the entity on.""" states = [pypck.lcn_defs.RelayStateModifier.NOCHANGE] * 8 states[self.output.value] = pypck.lcn_defs.RelayStateModifier.ON - if not await self.address_connection.control_relays(states): + if not await self.device_connection.control_relays(states): return self._is_on = True self.async_write_ha_state() @@ -118,7 +118,7 @@ class LcnRelaySwitch(LcnDevice, SwitchEntity): states = [pypck.lcn_defs.RelayStateModifier.NOCHANGE] * 8 states[self.output.value] = pypck.lcn_defs.RelayStateModifier.OFF - if not await self.address_connection.control_relays(states): + if not await self.device_connection.control_relays(states): return self._is_on = False self.async_write_ha_state()