diff --git a/custom_components/danfoss_ally/__init__.py b/custom_components/danfoss_ally/__init__.py index 7b8ad35..94b1450 100644 --- a/custom_components/danfoss_ally/__init__.py +++ b/custom_components/danfoss_ally/__init__.py @@ -92,6 +92,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): async def _update(now): """Periodic update.""" await allyconnector.async_update() + await _update(None) update_track = async_track_time_interval( @@ -165,14 +166,21 @@ async def async_update(self) -> None: """Update API data.""" _LOGGER.debug("Updating Danfoss Ally devices") - # Postpone poll if a recent change were made - Attempt to avoid UI glitches - seconds_since_write = (datetime.utcnow() - self._latest_write_time).total_seconds() - if (seconds_since_write < 1): - _LOGGER.debug("Seconds since last write %f. Postponing update for 1 sec.", seconds_since_write) + # Postpone poll if a recent change were made - Attempt to avoid UI glitches + seconds_since_write = ( + datetime.utcnow() - self._latest_write_time + ).total_seconds() + if seconds_since_write < 1: + _LOGGER.debug( + "Seconds since last write %f. Postponing update for 1 sec.", + seconds_since_write, + ) await asyncio.sleep(1) # Poll API - await self.hass.async_add_executor_job(self.ally.getDeviceList) #self.ally.getDeviceList() + await self.hass.async_add_executor_job( + self.ally.getDeviceList + ) # self.ally.getDeviceList() self._latest_poll_time = datetime.utcnow() for device in self.ally.devices: # pylint: disable=consider-using-dict-items @@ -184,15 +192,21 @@ def devices(self): """Return device list from API.""" return self.ally.devices - def set_temperature(self, device_id: str, temperature: float, code = "manual_mode_fast") -> None: + def set_temperature( + self, device_id: str, temperature: float, code="manual_mode_fast" + ) -> None: """Set temperature for device_id.""" self._latest_write_time = datetime.utcnow() self.ally.setTemperature(device_id, temperature, code) # Debug info - log if update was done approximately as the same time as write - seconds_since_poll = (datetime.utcnow() - self._latest_poll_time).total_seconds() - if (seconds_since_poll < 0.5): - _LOGGER.warn("set_temperature: Time since last poll %f sec.", seconds_since_poll) + seconds_since_poll = ( + datetime.utcnow() - self._latest_poll_time + ).total_seconds() + if seconds_since_poll < 0.5: + _LOGGER.warn( + "set_temperature: Time since last poll %f sec.", seconds_since_poll + ) def set_mode(self, device_id: str, mode: str) -> None: """Set operating mode for device_id.""" @@ -200,8 +214,10 @@ def set_mode(self, device_id: str, mode: str) -> None: self.ally.setMode(device_id, mode) # Debug info - log if update was done approximately as the same time as write - seconds_since_poll = (datetime.utcnow() - self._latest_poll_time).total_seconds() - if (seconds_since_poll < 0.5): + seconds_since_poll = ( + datetime.utcnow() - self._latest_poll_time + ).total_seconds() + if seconds_since_poll < 0.5: _LOGGER.warn("set_mode: Time since last poll %f sec.", seconds_since_poll) @property diff --git a/custom_components/danfoss_ally/binary_sensor.py b/custom_components/danfoss_ally/binary_sensor.py index 123d32c..3443ab5 100644 --- a/custom_components/danfoss_ally/binary_sensor.py +++ b/custom_components/danfoss_ally/binary_sensor.py @@ -32,7 +32,11 @@ async def async_setup_entry( entities.extend( [ AllyBinarySensor( - ally, ally.devices[device]["name"], device, "open window", ally.devices[device]["model"] + ally, + ally.devices[device]["name"], + device, + "open window", + ally.devices[device]["model"], ) ] ) @@ -43,7 +47,11 @@ async def async_setup_entry( entities.extend( [ AllyBinarySensor( - ally, ally.devices[device]["name"], device, "child lock", ally.devices[device]["model"] + ally, + ally.devices[device]["name"], + device, + "child lock", + ally.devices[device]["model"], ) ] ) @@ -54,18 +62,26 @@ async def async_setup_entry( entities.extend( [ AllyBinarySensor( - ally, ally.devices[device]["name"], device, "connectivity", ally.devices[device]["model"] + ally, + ally.devices[device]["name"], + device, + "connectivity", + ally.devices[device]["model"], ) ] ) - if 'banner_ctrl' in ally.devices[device]: + if "banner_ctrl" in ally.devices[device]: _LOGGER.debug( "Found banner_ctrl detector for %s", ally.devices[device]["name"] ) entities.extend( [ AllyBinarySensor( - ally, ally.devices[device]["name"], device, 'banner control', ally.devices[device]["model"] + ally, + ally.devices[device]["name"], + device, + "banner control", + ally.devices[device]["model"], ) ] ) @@ -166,4 +182,4 @@ def _async_update_data(self): elif self._type == "connectivity": self._state = bool(self._device["online"]) elif self._type == "banner control": - self._state = bool(self._device['banner_ctrl']) + self._state = bool(self._device["banner_ctrl"]) diff --git a/custom_components/danfoss_ally/climate.py b/custom_components/danfoss_ally/climate.py index a22b66c..b8c1a38 100644 --- a/custom_components/danfoss_ally/climate.py +++ b/custom_components/danfoss_ally/climate.py @@ -1,4 +1,5 @@ """Support for Danfoss Ally thermostats.""" +import functools as ft import logging import voluptuous as vol @@ -13,14 +14,13 @@ PRESET_AWAY, PRESET_HOME, SUPPORT_PRESET_MODE, - SUPPORT_TARGET_TEMPERATURE + SUPPORT_TARGET_TEMPERATURE, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_platform from homeassistant.helpers.dispatcher import async_dispatcher_connect -import functools as ft from . import AllyConnector from .const import ( @@ -72,7 +72,7 @@ def __init__( PRESET_PAUSE, PRESET_MANUAL, PRESET_HOLIDAY_HOME, - PRESET_HOLIDAY_AWAY + PRESET_HOLIDAY_AWAY, ] self._support_flags = support_flags @@ -204,7 +204,7 @@ def set_preset_mode(self, preset_mode): if mode is None: return - self._device["mode"] = mode # Update current copy of device data + self._device["mode"] = mode # Update current copy of device data self._ally.set_mode(self._device_id, mode) # Update UI @@ -242,19 +242,25 @@ def set_temperature(self, **kwargs): temperature = kwargs.get(ATTR_TEMPERATURE) if ATTR_PRESET_MODE in kwargs: - setpoint_code = self.get_setpoint_code_for_mode(kwargs.get(ATTR_PRESET_MODE)) # Preset_mode sent from action + setpoint_code = self.get_setpoint_code_for_mode( + kwargs.get(ATTR_PRESET_MODE) + ) # Preset_mode sent from action elif ATTR_HVAC_MODE in kwargs: - value = kwargs.get(ATTR_HVAC_MODE) # HVAC_mode sent from action + value = kwargs.get(ATTR_HVAC_MODE) # HVAC_mode sent from action if value == HVAC_MODE_AUTO: setpoint_code = self.get_setpoint_code_for_mode("at_home") if value == HVAC_MODE_HEAT: setpoint_code = self.get_setpoint_code_for_mode("manual") else: - setpoint_code = self.get_setpoint_code_for_mode(self._device["mode"]) # Current preset_mode + setpoint_code = self.get_setpoint_code_for_mode( + self._device["mode"] + ) # Current preset_mode changed = False if temperature is not None and setpoint_code is not None: - self._device[setpoint_code] = temperature # Update temperature in current copy + self._device[ + setpoint_code + ] = temperature # Update temperature in current copy self._ally.set_temperature(self._device_id, temperature, setpoint_code) changed = True @@ -307,15 +313,19 @@ def set_hvac_mode(self, hvac_mode): if mode is None: return - self._device["mode"] = mode # Update current copy of device data + self._device["mode"] = mode # Update current copy of device data self._ally.set_mode(self._device_id, mode) # Update UI self.async_write_ha_state() - def get_setpoint_code_for_mode(self, mode, for_writing = True): + def get_setpoint_code_for_mode(self, mode, for_writing=True): setpoint_code = None - if for_writing == False and "banner_ctrl" in self._device and bool(self._device['banner_ctrl']): + if ( + for_writing == False + and "banner_ctrl" in self._device + and bool(self._device["banner_ctrl"]) + ): # Temperature setpoint is overridden locally at the thermostate setpoint_code = "manual_mode_fast" elif mode == "at_home" or mode == "home": @@ -356,7 +366,7 @@ def __init__( heat_max_temp, heat_step, supported_hvac_modes, - support_flags + support_flags, ): """Initialize Danfoss Icon climate entity.""" super().__init__( @@ -368,7 +378,7 @@ def __init__( heat_max_temp, heat_step, supported_hvac_modes, - support_flags + support_flags, ) @property @@ -395,7 +405,7 @@ async def async_setup_entry( vol.Required("temperature"): vol.Coerce(float), vol.Optional("preset_mode"): str, }, - "set_preset_temperature" + "set_preset_temperature", ) ally: AllyConnector = hass.data[DOMAIN][entry.entry_id][DATA] @@ -411,7 +421,12 @@ def _generate_entities(ally: AllyConnector): for device in ally.devices: if ally.devices[device]["isThermostat"]: _LOGGER.debug("Found climate entity for %s", ally.devices[device]["name"]) - entity = create_climate_entity(ally, ally.devices[device]["name"], device, ally.devices[device]["model"]) + entity = create_climate_entity( + ally, + ally.devices[device]["name"], + device, + ally.devices[device]["model"], + ) if entity: entities.append(entity) return entities diff --git a/custom_components/danfoss_ally/device_action.py b/custom_components/danfoss_ally/device_action.py index e5c112c..d32bf6c 100644 --- a/custom_components/danfoss_ally/device_action.py +++ b/custom_components/danfoss_ally/device_action.py @@ -1,26 +1,28 @@ """Provides device automations for Climate.""" from __future__ import annotations -import logging -import voluptuous as vol import json +import logging +import homeassistant.helpers.config_validation as cv +import voluptuous as vol +from homeassistant.components.climate import ATTR_PRESET_MODE, ATTR_PRESET_MODES +from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN +from homeassistant.components.climate import SERVICE_SET_TEMPERATURE from homeassistant.const import ( ATTR_ENTITY_ID, + ATTR_TEMPERATURE, CONF_DEVICE_ID, CONF_DOMAIN, CONF_ENTITY_ID, CONF_TYPE, - ATTR_TEMPERATURE, ) from homeassistant.core import Context, HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import get_capability, get_supported_features -from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN, SERVICE_SET_TEMPERATURE, ATTR_PRESET_MODE, ATTR_PRESET_MODES -from .const import DOMAIN, ACTION_TYPE_SET_PRESET_TEMPERATURE, ATTR_SETPOINT +from .const import ACTION_TYPE_SET_PRESET_TEMPERATURE, ATTR_SETPOINT, DOMAIN _LOGGER = logging.getLogger(__name__) @@ -58,7 +60,10 @@ async def async_get_actions( CONF_ENTITY_ID: entry.entity_id, } - _LOGGER.debug("Action: " + json.dumps({**base_action, CONF_TYPE: ACTION_TYPE_SET_PRESET_TEMPERATURE})) + _LOGGER.debug( + "Action: " + + json.dumps({**base_action, CONF_TYPE: ACTION_TYPE_SET_PRESET_TEMPERATURE}) + ) actions.append({**base_action, CONF_TYPE: ACTION_TYPE_SET_PRESET_TEMPERATURE}) # if supported_features & const.SUPPORT_PRESET_MODE: @@ -78,7 +83,7 @@ async def async_call_action_from_config( service_data[ATTR_TEMPERATURE] = config[ATTR_TEMPERATURE] if ATTR_PRESET_MODE in config: service_data[ATTR_PRESET_MODE] = config[ATTR_PRESET_MODE] - domain = DOMAIN # danfoss_ally + domain = DOMAIN # danfoss_ally await hass.services.async_call( domain, service, service_data, blocking=True, context=context @@ -94,12 +99,11 @@ async def async_get_action_capabilities(hass, config): if action_type == ACTION_TYPE_SET_PRESET_TEMPERATURE: try: preset_modes = ( - get_capability(hass, config[ATTR_ENTITY_ID], ATTR_PRESET_MODES) - or [] + get_capability(hass, config[ATTR_ENTITY_ID], ATTR_PRESET_MODES) or [] ) except HomeAssistantError: preset_modes = [] - + preset_modes_kv = {} for entry in preset_modes: preset_modes_kv[entry.lower()] = entry.capitalize() diff --git a/custom_components/danfoss_ally/entity.py b/custom_components/danfoss_ally/entity.py index c3c18da..a776ad5 100644 --- a/custom_components/danfoss_ally/entity.py +++ b/custom_components/danfoss_ally/entity.py @@ -7,7 +7,7 @@ class AllyDeviceEntity(Entity): """Base implementation for Ally device.""" - def __init__(self, name, device_id, device_type, model = None): + def __init__(self, name, device_id, device_type, model=None): """Initialize a Ally device.""" super().__init__() self._type = device_type diff --git a/custom_components/danfoss_ally/sensor.py b/custom_components/danfoss_ally/sensor.py index 1099335..c653144 100644 --- a/custom_components/danfoss_ally/sensor.py +++ b/custom_components/danfoss_ally/sensor.py @@ -4,13 +4,13 @@ import logging from enum import IntEnum -from homeassistant.config_entries import ConfigEntry from homeassistant.components.sensor import ( SensorDeviceClass, SensorEntity, SensorEntityDescription, SensorStateClass, ) +from homeassistant.config_entries import ConfigEntry from homeassistant.const import PERCENTAGE, TEMP_CELSIUS from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -30,6 +30,7 @@ class AllySensorType(IntEnum): BATTERY = 1 HUMIDITY = 2 + SENSORS = [ SensorEntityDescription( key=AllySensorType.TEMPERATURE, @@ -54,7 +55,7 @@ class AllySensorType(IntEnum): native_unit_of_measurement=PERCENTAGE, state_class=SensorStateClass.MEASUREMENT, name="{} humidity", - ) + ), ] @@ -74,10 +75,17 @@ async def async_setup_entry( "Found %s sensor for %s", sensor_type, ally.devices[device]["name"] ) entities.extend( - [AllySensor(ally, ally.devices[device]["name"], device, sensor, ally.devices[device]["model"])] + [ + AllySensor( + ally, + ally.devices[device]["name"], + device, + sensor, + ally.devices[device]["model"], + ) + ] ) - if entities: async_add_entities(entities, True) @@ -86,7 +94,12 @@ class AllySensor(AllyDeviceEntity, SensorEntity): """Representation of an Ally sensor.""" def __init__( - self, ally: DanfossAlly, name, device_id, description: SensorEntityDescription, model = None + self, + ally: DanfossAlly, + name, + device_id, + description: SensorEntityDescription, + model=None, ): """Initialize Ally binary_sensor.""" self.entity_description = description @@ -97,7 +110,7 @@ def __init__( super().__init__(name, device_id, self._type, model) _LOGGER.debug("Device_id: %s --- Device: %s", self._device_id, self._device) - + self._attr_native_value = None self._attr_extra_state_attributes = None self._attr_name = self.entity_description.name.format(name) @@ -123,7 +136,9 @@ def _async_update_callback(self): @callback def _async_update_data(self): """Load data.""" - _LOGGER.debug("Loading new sensor data for Ally Sensor for device %s", self._device_id) + _LOGGER.debug( + "Loading new sensor data for Ally Sensor for device %s", self._device_id + ) self._device = self._ally.devices[self._device_id] if self._type in self._device: