From 799cdbe64d5edbe523546c4d586a605ff3e5bc06 Mon Sep 17 00:00:00 2001 From: shbatm Date: Sun, 10 May 2020 09:40:19 -0500 Subject: [PATCH] Add support for ISY994 Variables as Sensors (#35453) * Update tests, add missing constant * ISY994 Add support for ISY Variables as sensors --- homeassistant/components/isy994/__init__.py | 14 +++++++- .../components/isy994/config_flow.py | 6 ++++ homeassistant/components/isy994/const.py | 3 ++ homeassistant/components/isy994/helpers.py | 19 +++++++++++ homeassistant/components/isy994/sensor.py | 32 +++++++++++++++++-- homeassistant/components/isy994/strings.json | 3 +- .../components/isy994/translations/en.json | 3 +- tests/components/isy994/test_config_flow.py | 4 +++ 8 files changed, 79 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/isy994/__init__.py b/homeassistant/components/isy994/__init__.py index e75cb7659c0..d067f867cdd 100644 --- a/homeassistant/components/isy994/__init__.py +++ b/homeassistant/components/isy994/__init__.py @@ -20,19 +20,22 @@ from .const import ( CONF_RESTORE_LIGHT_STATE, CONF_SENSOR_STRING, CONF_TLS_VER, + CONF_VAR_SENSOR_STRING, DEFAULT_IGNORE_STRING, DEFAULT_RESTORE_LIGHT_STATE, DEFAULT_SENSOR_STRING, + DEFAULT_VAR_SENSOR_STRING, DOMAIN, ISY994_ISY, ISY994_NODES, ISY994_PROGRAMS, + ISY994_VARIABLES, MANUFACTURER, SUPPORTED_PLATFORMS, SUPPORTED_PROGRAM_PLATFORMS, UNDO_UPDATE_LISTENER, ) -from .helpers import _categorize_nodes, _categorize_programs +from .helpers import _categorize_nodes, _categorize_programs, _categorize_variables CONFIG_SCHEMA = vol.Schema( { @@ -48,6 +51,9 @@ CONFIG_SCHEMA = vol.Schema( vol.Optional( CONF_SENSOR_STRING, default=DEFAULT_SENSOR_STRING ): cv.string, + vol.Optional( + CONF_VAR_SENSOR_STRING, default=DEFAULT_VAR_SENSOR_STRING + ): cv.string, vol.Required( CONF_RESTORE_LIGHT_STATE, default=DEFAULT_RESTORE_LIGHT_STATE ): bool, @@ -111,6 +117,8 @@ async def async_setup_entry( for platform in SUPPORTED_PROGRAM_PLATFORMS: hass_isy_data[ISY994_PROGRAMS][platform] = [] + hass_isy_data[ISY994_VARIABLES] = [] + isy_config = entry.data isy_options = entry.options @@ -123,6 +131,9 @@ async def async_setup_entry( tls_version = isy_config.get(CONF_TLS_VER) ignore_identifier = isy_options.get(CONF_IGNORE_STRING, DEFAULT_IGNORE_STRING) sensor_identifier = isy_options.get(CONF_SENSOR_STRING, DEFAULT_SENSOR_STRING) + variable_identifier = isy_options.get( + CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING + ) if host.scheme == "http": https = False @@ -153,6 +164,7 @@ async def async_setup_entry( _categorize_nodes(hass_isy_data, isy.nodes, ignore_identifier, sensor_identifier) _categorize_programs(hass_isy_data, isy.programs) + _categorize_variables(hass_isy_data, isy.variables, variable_identifier) # Dump ISY Clock Information. Future: Add ISY as sensor to Hass with attrs _LOGGER.info(repr(isy.clock)) diff --git a/homeassistant/components/isy994/config_flow.py b/homeassistant/components/isy994/config_flow.py index cf78c6ddc47..3874cb58798 100644 --- a/homeassistant/components/isy994/config_flow.py +++ b/homeassistant/components/isy994/config_flow.py @@ -15,10 +15,12 @@ from .const import ( CONF_RESTORE_LIGHT_STATE, CONF_SENSOR_STRING, CONF_TLS_VER, + CONF_VAR_SENSOR_STRING, DEFAULT_IGNORE_STRING, DEFAULT_RESTORE_LIGHT_STATE, DEFAULT_SENSOR_STRING, DEFAULT_TLS_VERSION, + DEFAULT_VAR_SENSOR_STRING, ) from .const import DOMAIN # pylint:disable=unused-import @@ -154,11 +156,15 @@ class OptionsFlowHandler(config_entries.OptionsFlow): ) ignore_string = options.get(CONF_IGNORE_STRING, DEFAULT_IGNORE_STRING) sensor_string = options.get(CONF_SENSOR_STRING, DEFAULT_SENSOR_STRING) + var_sensor_string = options.get( + CONF_VAR_SENSOR_STRING, DEFAULT_VAR_SENSOR_STRING + ) options_schema = vol.Schema( { vol.Optional(CONF_IGNORE_STRING, default=ignore_string): str, vol.Optional(CONF_SENSOR_STRING, default=sensor_string): str, + vol.Optional(CONF_VAR_SENSOR_STRING, default=var_sensor_string): str, vol.Required( CONF_RESTORE_LIGHT_STATE, default=restore_light_state ): bool, diff --git a/homeassistant/components/isy994/const.py b/homeassistant/components/isy994/const.py index 468d1be5650..a3ad9798511 100644 --- a/homeassistant/components/isy994/const.py +++ b/homeassistant/components/isy994/const.py @@ -98,6 +98,7 @@ MANUFACTURER = "Universal Devices, Inc" CONF_IGNORE_STRING = "ignore_string" CONF_SENSOR_STRING = "sensor_string" +CONF_VAR_SENSOR_STRING = "variable_sensor_string" CONF_TLS_VER = "tls" CONF_RESTORE_LIGHT_STATE = "restore_light_state" @@ -106,6 +107,7 @@ DEFAULT_SENSOR_STRING = "sensor" DEFAULT_RESTORE_LIGHT_STATE = False DEFAULT_TLS_VERSION = 1.1 DEFAULT_PROGRAM_STRING = "HA." +DEFAULT_VAR_SENSOR_STRING = "HA." KEY_ACTIONS = "actions" KEY_STATUS = "status" @@ -122,6 +124,7 @@ ISY_GROUP_PLATFORM = SWITCH ISY994_ISY = "isy" ISY994_NODES = "isy994_nodes" ISY994_PROGRAMS = "isy994_programs" +ISY994_VARIABLES = "isy994_variables" FILTER_UOM = "uom" FILTER_STATES = "states" diff --git a/homeassistant/components/isy994/helpers.py b/homeassistant/components/isy994/helpers.py index d1265eff508..a62611ff2da 100644 --- a/homeassistant/components/isy994/helpers.py +++ b/homeassistant/components/isy994/helpers.py @@ -10,6 +10,7 @@ from pyisy.constants import ( ) from pyisy.nodes import Group, Node, Nodes from pyisy.programs import Programs +from pyisy.variables import Variables from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR from homeassistant.components.climate.const import DOMAIN as CLIMATE @@ -31,6 +32,7 @@ from .const import ( FILTER_ZWAVE_CAT, ISY994_NODES, ISY994_PROGRAMS, + ISY994_VARIABLES, ISY_GROUP_PLATFORM, KEY_ACTIONS, KEY_STATUS, @@ -345,6 +347,23 @@ def _categorize_programs(hass_isy_data: dict, programs: Programs) -> None: hass_isy_data[ISY994_PROGRAMS][platform].append(entity) +def _categorize_variables( + hass_isy_data: dict, variables: Variables, identifier: str +) -> None: + """Gather the ISY994 Variables to be added as sensors.""" + try: + var_to_add = [ + (vtype, vname, vid) + for (vtype, vname, vid) in variables.children + if identifier in vname + ] + except KeyError as err: + _LOGGER.error("Error adding ISY Variables: %s", err) + return + for vtype, vname, vid in var_to_add: + hass_isy_data[ISY994_VARIABLES].append((vname, variables[vtype][vid])) + + async def migrate_old_unique_ids( hass: HomeAssistantType, platform: str, devices: Optional[List[Any]] ) -> None: diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index 838020026db..72999b1d231 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -1,5 +1,5 @@ """Support for ISY994 sensors.""" -from typing import Callable +from typing import Callable, Dict from pyisy.constants import ISY_VALUE_UNKNOWN @@ -12,10 +12,11 @@ from .const import ( _LOGGER, DOMAIN as ISY994_DOMAIN, ISY994_NODES, + ISY994_VARIABLES, UOM_FRIENDLY_NAME, UOM_TO_STATES, ) -from .entity import ISYNodeEntity +from .entity import ISYEntity, ISYNodeEntity from .helpers import migrate_old_unique_ids @@ -32,6 +33,9 @@ async def async_setup_entry( _LOGGER.debug("Loading %s", node.name) devices.append(ISYSensorEntity(node)) + for vname, vobj in hass_isy_data[ISY994_VARIABLES]: + devices.append(ISYSensorVariableEntity(vname, vobj)) + await migrate_old_unique_ids(hass, SENSOR, devices) async_add_entities(devices) @@ -84,3 +88,27 @@ class ISYSensorEntity(ISYNodeEntity): if raw_units in (TEMP_FAHRENHEIT, TEMP_CELSIUS): return self.hass.config.units.temperature_unit return raw_units + + +class ISYSensorVariableEntity(ISYEntity): + """Representation of an ISY994 variable as a sensor device.""" + + def __init__(self, vname: str, vobj: object) -> None: + """Initialize the ISY994 binary sensor program.""" + super().__init__(vobj) + self._name = vname + + @property + def state(self): + """Return the state of the variable.""" + return self.value + + @property + def device_state_attributes(self) -> Dict: + """Get the state attributes for the device.""" + return {"init_value": int(self._node.init)} + + @property + def icon(self): + """Return the icon.""" + return "mdi:counter" diff --git a/homeassistant/components/isy994/strings.json b/homeassistant/components/isy994/strings.json index 11f1dcfb2b0..61c9a807793 100644 --- a/homeassistant/components/isy994/strings.json +++ b/homeassistant/components/isy994/strings.json @@ -27,10 +27,11 @@ "step": { "init": { "title": "ISY994 Options", - "description": "Set the options for the ISY Integration: \n • Node Sensor String: Any device or folder that contains 'Node Sensor String' in the name will be treated as a sensor or binary sensor. \n • Ignore String: Any device with 'Ignore String' in the name will be ignored. \n • Restore Light Brightness: If enabled, the previous brightness will be restored when turning on a light instead of the device's built-in On-Level.", + "description": "Set the options for the ISY Integration: \n • Node Sensor String: Any device or folder that contains 'Node Sensor String' in the name will be treated as a sensor or binary sensor. \n • Ignore String: Any device with 'Ignore String' in the name will be ignored. \n • Variable Sensor String: Any variable that contains 'Variable Sensor String' will be added as a sensor. \n • Restore Light Brightness: If enabled, the previous brightness will be restored when turning on a light instead of the device's built-in On-Level.", "data": { "sensor_string": "Node Sensor String", "ignore_string": "Ignore String", + "variable_sensor_string": "Variable Sensor String", "restore_light_state": "Restore Light Brightness" } } diff --git a/homeassistant/components/isy994/translations/en.json b/homeassistant/components/isy994/translations/en.json index 4ad14d99c63..0d65c07ebb4 100644 --- a/homeassistant/components/isy994/translations/en.json +++ b/homeassistant/components/isy994/translations/en.json @@ -27,10 +27,11 @@ "step": { "init": { "title": "ISY994 Options", - "description": "Set the options for the ISY Integration: \n • Node Sensor String: Any device or folder that contains 'Node Sensor String' in the name will be treated as a sensor or binary sensor. \n • Ignore String: Any device with 'Ignore String' in the name will be ignored. \n • Restore Light Brightness: If enabled, the previous brightness will be restored when turning on a light instead of the device's built-in On-Level.", + "description": "Set the options for the ISY Integration: \n • Node Sensor String: Any device or folder that contains 'Node Sensor String' in the name will be treated as a sensor or binary sensor. \n • Ignore String: Any device with 'Ignore String' in the name will be ignored. \n • Variable Sensor String: Any variable that contains 'Variable Sensor String' will be added as a sensor. \n • Restore Light Brightness: If enabled, the previous brightness will be restored when turning on a light instead of the device's built-in On-Level.", "data": { "sensor_string": "Node Sensor String", "ignore_string": "Ignore String", + "variable_sensor_string" : "Variable Sensor String", "restore_light_state": "Restore Light Brightness" } } diff --git a/tests/components/isy994/test_config_flow.py b/tests/components/isy994/test_config_flow.py index c1ca0ab4b5f..0a6a5ca6d66 100644 --- a/tests/components/isy994/test_config_flow.py +++ b/tests/components/isy994/test_config_flow.py @@ -7,6 +7,7 @@ from homeassistant.components.isy994.const import ( CONF_RESTORE_LIGHT_STATE, CONF_SENSOR_STRING, CONF_TLS_VER, + CONF_VAR_SENSOR_STRING, DOMAIN, ) from homeassistant.config_entries import SOURCE_IMPORT @@ -25,6 +26,7 @@ MOCK_TLS_VERSION = 1.2 MOCK_IGNORE_STRING = "{IGNOREME}" MOCK_RESTORE_LIGHT_STATE = True MOCK_SENSOR_STRING = "IMASENSOR" +MOCK_VARIABLE_SENSOR_STRING = "HomeAssistant." MOCK_USER_INPUT = { "host": f"http://{MOCK_HOSTNAME}", @@ -45,6 +47,7 @@ MOCK_IMPORT_FULL_CONFIG = { CONF_RESTORE_LIGHT_STATE: MOCK_RESTORE_LIGHT_STATE, CONF_SENSOR_STRING: MOCK_SENSOR_STRING, CONF_TLS_VER: MOCK_TLS_VERSION, + CONF_VAR_SENSOR_STRING: MOCK_VARIABLE_SENSOR_STRING, } MOCK_DEVICE_NAME = "Name of the device" @@ -203,4 +206,5 @@ async def test_import_flow_all_fields(hass: HomeAssistantType) -> None: assert result["data"][CONF_IGNORE_STRING] == MOCK_IGNORE_STRING assert result["data"][CONF_RESTORE_LIGHT_STATE] == MOCK_RESTORE_LIGHT_STATE assert result["data"][CONF_SENSOR_STRING] == MOCK_SENSOR_STRING + assert result["data"][CONF_VAR_SENSOR_STRING] == MOCK_VARIABLE_SENSOR_STRING assert result["data"][CONF_TLS_VER] == MOCK_TLS_VERSION