From 872167521856df8c6df03230521bfaae9bd3c015 Mon Sep 17 00:00:00 2001 From: jan iversen Date: Mon, 24 May 2021 20:13:25 +0200 Subject: [PATCH] Use BaseSwitch class in modbus switch/fan/light (#51031) --- .../components/modbus/base_platform.py | 121 +++++++++++++++- homeassistant/components/modbus/fan.py | 134 +----------------- homeassistant/components/modbus/light.py | 132 +---------------- homeassistant/components/modbus/switch.py | 129 +---------------- 4 files changed, 136 insertions(+), 380 deletions(-) diff --git a/homeassistant/components/modbus/base_platform.py b/homeassistant/components/modbus/base_platform.py index d6811bca1ff..ed2f6e69863 100644 --- a/homeassistant/components/modbus/base_platform.py +++ b/homeassistant/components/modbus/base_platform.py @@ -8,15 +8,29 @@ from typing import Any from homeassistant.const import ( CONF_ADDRESS, + CONF_COMMAND_OFF, + CONF_COMMAND_ON, + CONF_DELAY, CONF_DEVICE_CLASS, CONF_NAME, CONF_SCAN_INTERVAL, CONF_SLAVE, + STATE_ON, ) from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import async_track_time_interval +from homeassistant.helpers.event import async_call_later, async_track_time_interval +from homeassistant.helpers.restore_state import RestoreEntity -from .const import CONF_INPUT_TYPE +from .const import ( + CALL_TYPE_COIL, + CALL_TYPE_WRITE_COIL, + CALL_TYPE_WRITE_REGISTER, + CONF_INPUT_TYPE, + CONF_STATE_OFF, + CONF_STATE_ON, + CONF_VERIFY, + CONF_WRITE_TYPE, +) from .modbus import ModbusHub PARALLEL_UPDATES = 1 @@ -68,3 +82,106 @@ class BasePlatform(Entity): def available(self) -> bool: """Return True if entity is available.""" return self._available + + +class BaseSwitch(BasePlatform, RestoreEntity): + """Base class representing a Modbus switch.""" + + def __init__(self, hub: ModbusHub, config: dict) -> None: + """Initialize the switch.""" + config[CONF_INPUT_TYPE] = "" + super().__init__(hub, config) + self._is_on = None + if config[CONF_WRITE_TYPE] == CALL_TYPE_COIL: + self._write_type = CALL_TYPE_WRITE_COIL + else: + self._write_type = CALL_TYPE_WRITE_REGISTER + self.command_on = config[CONF_COMMAND_ON] + self._command_off = config[CONF_COMMAND_OFF] + if CONF_VERIFY in config: + if config[CONF_VERIFY] is None: + config[CONF_VERIFY] = {} + self._verify_active = True + self._verify_delay = config[CONF_VERIFY].get(CONF_DELAY, 0) + self._verify_address = config[CONF_VERIFY].get( + CONF_ADDRESS, config[CONF_ADDRESS] + ) + self._verify_type = config[CONF_VERIFY].get( + CONF_INPUT_TYPE, config[CONF_WRITE_TYPE] + ) + self._state_on = config[CONF_VERIFY].get(CONF_STATE_ON, self.command_on) + self._state_off = config[CONF_VERIFY].get(CONF_STATE_OFF, self._command_off) + else: + self._verify_active = False + + async def async_added_to_hass(self): + """Handle entity which will be added.""" + await self.async_base_added_to_hass() + state = await self.async_get_last_state() + if state: + self._is_on = state.state == STATE_ON + + @property + def is_on(self): + """Return true if switch is on.""" + return self._is_on + + async def async_turn(self, command): + """Evaluate switch result.""" + result = await self._hub.async_pymodbus_call( + self._slave, self._address, command, self._write_type + ) + if result is None: + self._available = False + self.async_write_ha_state() + return + + self._available = True + if not self._verify_active: + self._is_on = command == self.command_on + self.async_write_ha_state() + return + + if self._verify_delay: + async_call_later(self.hass, self._verify_delay, self.async_update) + else: + await self.async_update() + + async def async_turn_off(self, **kwargs): + """Set switch off.""" + await self.async_turn(self._command_off) + + async def async_update(self, now=None): + """Update the entity state.""" + # remark "now" is a dummy parameter to avoid problems with + # async_track_time_interval + if not self._verify_active: + self._available = True + self.async_write_ha_state() + return + + result = await self._hub.async_pymodbus_call( + self._slave, self._verify_address, 1, self._verify_type + ) + if result is None: + self._available = False + self.async_write_ha_state() + return + + self._available = True + if self._verify_type == CALL_TYPE_COIL: + self._is_on = bool(result.bits[0] & 1) + else: + value = int(result.registers[0]) + if value == self._state_on: + self._is_on = True + elif value == self._state_off: + self._is_on = False + elif value is not None: + _LOGGER.error( + "Unexpected response from modbus device slave %s register %s, got 0x%2x", + self._slave, + self._verify_address, + value, + ) + self.async_write_ha_state() diff --git a/homeassistant/components/modbus/fan.py b/homeassistant/components/modbus/fan.py index 9e23e0291f1..7fabff25711 100644 --- a/homeassistant/components/modbus/fan.py +++ b/homeassistant/components/modbus/fan.py @@ -4,30 +4,12 @@ from __future__ import annotations import logging from homeassistant.components.fan import FanEntity -from homeassistant.const import ( - CONF_ADDRESS, - CONF_COMMAND_OFF, - CONF_COMMAND_ON, - CONF_NAME, - STATE_ON, -) +from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant -from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType -from .base_platform import BasePlatform -from .const import ( - CALL_TYPE_COIL, - CALL_TYPE_WRITE_COIL, - CALL_TYPE_WRITE_REGISTER, - CONF_FANS, - CONF_INPUT_TYPE, - CONF_STATE_OFF, - CONF_STATE_ON, - CONF_VERIFY, - CONF_WRITE_TYPE, - MODBUS_DOMAIN, -) +from .base_platform import BaseSwitch +from .const import CONF_FANS, MODBUS_DOMAIN from .modbus import ModbusHub PARALLEL_UPDATES = 1 @@ -48,46 +30,8 @@ async def async_setup_platform( async_add_entities(fans) -class ModbusFan(BasePlatform, FanEntity, RestoreEntity): - """Base class representing a Modbus fan.""" - - def __init__(self, hub: ModbusHub, config: dict) -> None: - """Initialize the fan.""" - config[CONF_INPUT_TYPE] = "" - super().__init__(hub, config) - self._is_on: bool = False - if config[CONF_WRITE_TYPE] == CALL_TYPE_COIL: - self._write_type = CALL_TYPE_WRITE_COIL - else: - self._write_type = CALL_TYPE_WRITE_REGISTER - self._command_on = config[CONF_COMMAND_ON] - self._command_off = config[CONF_COMMAND_OFF] - if CONF_VERIFY in config: - if config[CONF_VERIFY] is None: - config[CONF_VERIFY] = {} - self._verify_active = True - self._verify_address = config[CONF_VERIFY].get( - CONF_ADDRESS, config[CONF_ADDRESS] - ) - self._verify_type = config[CONF_VERIFY].get( - CONF_INPUT_TYPE, config[CONF_WRITE_TYPE] - ) - self._state_on = config[CONF_VERIFY].get(CONF_STATE_ON, self._command_on) - self._state_off = config[CONF_VERIFY].get(CONF_STATE_OFF, self._command_off) - else: - self._verify_active = False - - async def async_added_to_hass(self): - """Handle entity which will be added.""" - await self.async_base_added_to_hass() - state = await self.async_get_last_state() - if state: - self._is_on = state.state == STATE_ON - - @property - def is_on(self): - """Return true if fan is on.""" - return self._is_on +class ModbusFan(BaseSwitch, FanEntity): + """Class representing a Modbus fan.""" async def async_turn_on( self, @@ -97,70 +41,4 @@ class ModbusFan(BasePlatform, FanEntity, RestoreEntity): **kwargs, ) -> None: """Set fan on.""" - - result = await self._hub.async_pymodbus_call( - self._slave, self._address, self._command_on, self._write_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - return - - self._available = True - if self._verify_active: - await self.async_update() - return - - self._is_on = True - self.async_write_ha_state() - - async def async_turn_off(self, **kwargs): - """Set fan off.""" - result = await self._hub.async_pymodbus_call( - self._slave, self._address, self._command_off, self._write_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - else: - self._available = True - if self._verify_active: - await self.async_update() - else: - self._is_on = False - self.async_write_ha_state() - - async def async_update(self, now=None): - """Update the entity state.""" - # remark "now" is a dummy parameter to avoid problems with - # async_track_time_interval - if not self._verify_active: - self._available = True - self.async_write_ha_state() - return - - result = await self._hub.async_pymodbus_call( - self._slave, self._verify_address, 1, self._verify_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - return - - self._available = True - if self._verify_type == CALL_TYPE_COIL: - self._is_on = bool(result.bits[0] & 1) - else: - value = int(result.registers[0]) - if value == self._state_on: - self._is_on = True - elif value == self._state_off: - self._is_on = False - elif value is not None: - _LOGGER.error( - "Unexpected response from modbus device slave %s register %s, got 0x%2x", - self._slave, - self._verify_address, - value, - ) - self.async_write_ha_state() + await self.async_turn(self.command_on) diff --git a/homeassistant/components/modbus/light.py b/homeassistant/components/modbus/light.py index e1dfba40176..f56b01ff001 100644 --- a/homeassistant/components/modbus/light.py +++ b/homeassistant/components/modbus/light.py @@ -4,30 +4,12 @@ from __future__ import annotations import logging from homeassistant.components.light import LightEntity -from homeassistant.const import ( - CONF_ADDRESS, - CONF_COMMAND_OFF, - CONF_COMMAND_ON, - CONF_LIGHTS, - CONF_NAME, - STATE_ON, -) +from homeassistant.const import CONF_LIGHTS, CONF_NAME from homeassistant.core import HomeAssistant -from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType -from .base_platform import BasePlatform -from .const import ( - CALL_TYPE_COIL, - CALL_TYPE_WRITE_COIL, - CALL_TYPE_WRITE_REGISTER, - CONF_INPUT_TYPE, - CONF_STATE_OFF, - CONF_STATE_ON, - CONF_VERIFY, - CONF_WRITE_TYPE, - MODBUS_DOMAIN, -) +from .base_platform import BaseSwitch +from .const import MODBUS_DOMAIN from .modbus import ModbusHub PARALLEL_UPDATES = 1 @@ -47,111 +29,9 @@ async def async_setup_platform( async_add_entities(lights) -class ModbusLight(BasePlatform, LightEntity, RestoreEntity): - """Base class representing a Modbus light.""" - - def __init__(self, hub: ModbusHub, config: dict) -> None: - """Initialize the light.""" - config[CONF_INPUT_TYPE] = "" - super().__init__(hub, config) - self._is_on = None - if config[CONF_WRITE_TYPE] == CALL_TYPE_COIL: - self._write_type = CALL_TYPE_WRITE_COIL - else: - self._write_type = CALL_TYPE_WRITE_REGISTER - self._command_on = config[CONF_COMMAND_ON] - self._command_off = config[CONF_COMMAND_OFF] - if CONF_VERIFY in config: - if config[CONF_VERIFY] is None: - config[CONF_VERIFY] = {} - self._verify_active = True - self._verify_address = config[CONF_VERIFY].get( - CONF_ADDRESS, config[CONF_ADDRESS] - ) - self._verify_type = config[CONF_VERIFY].get( - CONF_INPUT_TYPE, config[CONF_WRITE_TYPE] - ) - self._state_on = config[CONF_VERIFY].get(CONF_STATE_ON, self._command_on) - self._state_off = config[CONF_VERIFY].get(CONF_STATE_OFF, self._command_off) - else: - self._verify_active = False - - async def async_added_to_hass(self): - """Handle entity which will be added.""" - await self.async_base_added_to_hass() - state = await self.async_get_last_state() - if state: - self._is_on = state.state == STATE_ON - - @property - def is_on(self): - """Return true if light is on.""" - return self._is_on +class ModbusLight(BaseSwitch, LightEntity): + """Class representing a Modbus light.""" async def async_turn_on(self, **kwargs): """Set light on.""" - - result = await self._hub.async_pymodbus_call( - self._slave, self._address, self._command_on, self._write_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - else: - self._available = True - if self._verify_active: - await self.async_update() - else: - self._is_on = True - self.async_write_ha_state() - - async def async_turn_off(self, **kwargs): - """Set light off.""" - result = await self._hub.async_pymodbus_call( - self._slave, self._address, self._command_off, self._write_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - else: - self._available = True - if self._verify_active: - await self.async_update() - else: - self._is_on = False - self.async_write_ha_state() - - async def async_update(self, now=None): - """Update the entity state.""" - # remark "now" is a dummy parameter to avoid problems with - # async_track_time_interval - if not self._verify_active: - self._available = True - self.async_write_ha_state() - return - - result = await self._hub.async_pymodbus_call( - self._slave, self._verify_address, 1, self._verify_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - return - - self._available = True - if self._verify_type == CALL_TYPE_COIL: - self._is_on = bool(result.bits[0] & 1) - else: - value = int(result.registers[0]) - if value == self._state_on: - self._is_on = True - elif value == self._state_off: - self._is_on = False - elif value is not None: - _LOGGER.error( - "Unexpected response from modbus device slave %s register %s, got 0x%2x", - self._slave, - self._verify_address, - value, - ) - self.async_write_ha_state() + await self.async_turn(self.command_on) diff --git a/homeassistant/components/modbus/switch.py b/homeassistant/components/modbus/switch.py index a9a4994a90b..98e15d5b311 100644 --- a/homeassistant/components/modbus/switch.py +++ b/homeassistant/components/modbus/switch.py @@ -4,32 +4,12 @@ from __future__ import annotations import logging from homeassistant.components.switch import SwitchEntity -from homeassistant.const import ( - CONF_ADDRESS, - CONF_COMMAND_OFF, - CONF_COMMAND_ON, - CONF_DELAY, - CONF_NAME, - CONF_SWITCHES, - STATE_ON, -) +from homeassistant.const import CONF_NAME, CONF_SWITCHES from homeassistant.core import HomeAssistant -from homeassistant.helpers.event import async_call_later -from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import ConfigType -from .base_platform import BasePlatform -from .const import ( - CALL_TYPE_COIL, - CALL_TYPE_WRITE_COIL, - CALL_TYPE_WRITE_REGISTER, - CONF_INPUT_TYPE, - CONF_STATE_OFF, - CONF_STATE_ON, - CONF_VERIFY, - CONF_WRITE_TYPE, - MODBUS_DOMAIN, -) +from .base_platform import BaseSwitch +from .const import MODBUS_DOMAIN from .modbus import ModbusHub PARALLEL_UPDATES = 1 @@ -48,108 +28,9 @@ async def async_setup_platform( async_add_entities(switches) -class ModbusSwitch(BasePlatform, SwitchEntity, RestoreEntity): +class ModbusSwitch(BaseSwitch, SwitchEntity): """Base class representing a Modbus switch.""" - def __init__(self, hub: ModbusHub, config: dict) -> None: - """Initialize the switch.""" - config[CONF_INPUT_TYPE] = "" - super().__init__(hub, config) - self._is_on = None - if config[CONF_WRITE_TYPE] == CALL_TYPE_COIL: - self._write_type = CALL_TYPE_WRITE_COIL - else: - self._write_type = CALL_TYPE_WRITE_REGISTER - self._command_on = config[CONF_COMMAND_ON] - self._command_off = config[CONF_COMMAND_OFF] - if CONF_VERIFY in config: - if config[CONF_VERIFY] is None: - config[CONF_VERIFY] = {} - self._verify_active = True - self._verify_delay = config[CONF_VERIFY].get(CONF_DELAY, 0) - self._verify_address = config[CONF_VERIFY].get( - CONF_ADDRESS, config[CONF_ADDRESS] - ) - self._verify_type = config[CONF_VERIFY].get( - CONF_INPUT_TYPE, config[CONF_WRITE_TYPE] - ) - self._state_on = config[CONF_VERIFY].get(CONF_STATE_ON, self._command_on) - self._state_off = config[CONF_VERIFY].get(CONF_STATE_OFF, self._command_off) - else: - self._verify_active = False - - async def async_added_to_hass(self): - """Handle entity which will be added.""" - await self.async_base_added_to_hass() - state = await self.async_get_last_state() - if state: - self._is_on = state.state == STATE_ON - - @property - def is_on(self): - """Return true if switch is on.""" - return self._is_on - - async def _async_turn(self, command): - """Evaluate switch result.""" - result = await self._hub.async_pymodbus_call( - self._slave, self._address, command, self._write_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - return - - self._available = True - if not self._verify_active: - self._is_on = command == self._command_on - self.async_write_ha_state() - return - - if self._verify_delay: - async_call_later(self.hass, self._verify_delay, self.async_update) - else: - await self.async_update() - async def async_turn_on(self, **kwargs): """Set switch on.""" - await self._async_turn(self._command_on) - - async def async_turn_off(self, **kwargs): - """Set switch off.""" - await self._async_turn(self._command_off) - - async def async_update(self, now=None): - """Update the entity state.""" - # remark "now" is a dummy parameter to avoid problems with - # async_track_time_interval - if not self._verify_active: - self._available = True - self.async_write_ha_state() - return - - result = await self._hub.async_pymodbus_call( - self._slave, self._verify_address, 1, self._verify_type - ) - if result is None: - self._available = False - self.async_write_ha_state() - return - - self._available = True - if self._verify_type == CALL_TYPE_COIL: - self._is_on = bool(result.bits[0] & 1) - else: - value = int(result.registers[0]) - if value == self._state_on: - self._is_on = True - elif value == self._state_off: - self._is_on = False - elif value is not None: - _LOGGER.error( - "Unexpected response from modbus device slave %s register %s, got 0x%2x", - self._slave, - self._verify_address, - value, - ) - self.async_write_ha_state() + await self.async_turn(self.command_on)