From 8a6dadf53e6447bd4d1b5c6463ea15a7ad2473b3 Mon Sep 17 00:00:00 2001 From: Jean-Marc Collin Date: Tue, 13 Jun 2023 21:41:59 +0000 Subject: [PATCH] Bug fixes after first integration run --- custom_components/solar_optimizer/__init__.py | 9 +++-- .../solar_optimizer/binary_sensor.py | 2 ++ .../solar_optimizer/coordinator.py | 33 ++++++++----------- .../solar_optimizer/managed_device.py | 26 ++++++++++++--- .../simulated_annealing_algo.py | 16 ++++----- 5 files changed, 51 insertions(+), 35 deletions(-) diff --git a/custom_components/solar_optimizer/__init__.py b/custom_components/solar_optimizer/__init__.py index ace7b21..2a23549 100644 --- a/custom_components/solar_optimizer/__init__.py +++ b/custom_components/solar_optimizer/__init__.py @@ -1,6 +1,7 @@ """Initialisation du package de l'intégration HACS Tuto""" import logging +from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.core import HomeAssistant from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.typing import ConfigType @@ -33,11 +34,15 @@ async def async_setup( hass, solar_optimizer_config ) - await coordinator.async_config_entry_first_refresh() - await async_setup_entry_sensor(hass) await async_setup_entry_binary_sensor(hass) + # refresh data on startup + async def _internal_startup(*_): + await coordinator.async_config_entry_first_refresh() + + hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _internal_startup) + # Return boolean to indicate that initialization was successful. return True diff --git a/custom_components/solar_optimizer/binary_sensor.py b/custom_components/solar_optimizer/binary_sensor.py index 5d53ef9..2275b3c 100644 --- a/custom_components/solar_optimizer/binary_sensor.py +++ b/custom_components/solar_optimizer/binary_sensor.py @@ -56,6 +56,8 @@ def update_custom_attributes(self, device): "can_change_power": device.can_change_power, "current_power": device.current_power, "requested_power": device.requested_power, + "duration_sec": device.duration_sec, + "duration_power_sec": device.duration_power_sec, "next_date_available": device.next_date_available.astimezone( current_tz ).isoformat(), diff --git a/custom_components/solar_optimizer/coordinator.py b/custom_components/solar_optimizer/coordinator.py index e1b9189..f28df76 100644 --- a/custom_components/solar_optimizer/coordinator.py +++ b/custom_components/solar_optimizer/coordinator.py @@ -92,29 +92,22 @@ async def _async_update_data(self): for _, device in enumerate(self._devices): # Initialize current power if not set and is active if device.is_active and device.current_power == 0: - device.init_power( - device.power_max if device.can_change_power else device.power_min - ) + power = device.power_max + if device.can_change_power: + state = self.hass.states.get(device.power_entity_id) + if power is not None: + power = round( + float(state.state) * device.convert_power_divide_factor + ) + else: + power = device.power_min + device.reset_next_date_available() + device.reset_next_date_available_power() + + device.init_power(power) if not device.is_active: device.init_power(0) - # calculate a device_states (not used) - # device_states[device.name] = { - # "name": device.name, - # "is_active": device.is_active, - # "is_usable": device.is_usable, - # "is_waiting": device.is_waiting, - # "current_power": device.current_power, - # "requested_power": device.requested_power, - # } - # _LOGGER.debug( - # "Evaluation of %s, device_active: %s, device_usable: %s", - # device.name, - # device.is_active, - # device.is_usable, - # ) - # calculated_data["device_states"] = device_states - # Add a power_consumption and power_production calculated_data["power_production"] = get_safe_float( self.hass, self._power_production_entity_id diff --git a/custom_components/solar_optimizer/managed_device.py b/custom_components/solar_optimizer/managed_device.py index 904a742..41b60f7 100644 --- a/custom_components/solar_optimizer/managed_device.py +++ b/custom_components/solar_optimizer/managed_device.py @@ -187,8 +187,10 @@ async def _apply_action(self, action_type: str, requested_power=None): entity_id = self._entity_id if action_type == ACTION_ACTIVATE: method = self._activation_service + self.reset_next_date_available() elif action_type == ACTION_DEACTIVATE: method = self._deactivation_service + self.reset_next_date_available() elif action_type == ACTION_CHANGE_POWER: assert ( self._can_change_power @@ -221,8 +223,6 @@ async def _apply_action(self, action_type: str, requested_power=None): ) self._current_power = self._requested_power - if action_type == ACTION_ACTIVATE or action_type == ACTION_DEACTIVATE: - self.reset_next_date_available() async def activate(self, requested_power=None): """Use this method to activate this ManagedDevice""" @@ -258,6 +258,9 @@ def reset_next_date_available_power(self): def init_power(self, power: int): """Initialise current_power and requested_power to the given value""" + _LOGGER.debug( + "Initializing power for entity '%s' with %s value", self._name, power + ) self._requested_power = self._current_power = power @property @@ -328,11 +331,21 @@ def duration_sec(self) -> int: """The duration a device is not available after a change of the managed device""" return self._duration_sec + @property + def duration_power_sec(self) -> int: + """The duration a device is not available after a change of the managed device for power change""" + return self._duration_power_sec + @property def entity_id(self) -> str: """The entity_id of the device""" return self._entity_id + @property + def power_entity_id(self) -> str: + """The entity_id of the device which gives the current power""" + return self._power_entity_id + @property def current_power(self) -> int: """The current_power of the device""" @@ -350,10 +363,15 @@ def can_change_power(self) -> bool: @property def next_date_available(self) -> datetime: - """true is the device can change its power""" + """returns the next available date for state change""" return self._next_date_available @property def next_date_available_power(self) -> datetime: - """true is the device can change its power""" + """return the next available date for power change""" return self._next_date_available_power + + @property + def convert_power_divide_factor(self) -> int: + """return""" + return self._convert_power_divide_factor diff --git a/custom_components/solar_optimizer/simulated_annealing_algo.py b/custom_components/solar_optimizer/simulated_annealing_algo.py index e8df0f6..8019bb7 100644 --- a/custom_components/solar_optimizer/simulated_annealing_algo.py +++ b/custom_components/solar_optimizer/simulated_annealing_algo.py @@ -135,16 +135,14 @@ def recuit_simule( # Calculer les objectifs pour la solution actuelle et le voisin objectif_voisin = self.calculer_objectif(voisin) if DEBUG: - _LOGGER.debug("Objecttif voisin : %2.f", objectif_voisin) + _LOGGER.debug("Objectif voisin : %2.f", objectif_voisin) # Accepter le voisin si son objectif est meilleur ou si la consommation totale n'excède pas la production solaire if objectif_voisin < objectif_actuel: - if DEBUG: - _LOGGER.debug("---> On garde l'objectif voisin") + _LOGGER.debug("---> On garde l'objectif voisin") solution_actuelle = voisin if objectif_voisin < self.calculer_objectif(meilleure_solution): - if DEBUG: - _LOGGER.debug("---> C'est la meilleure jusque là") + _LOGGER.debug("---> C'est la meilleure jusque là") meilleure_solution = voisin meilleure_objectif = objectif_voisin else: @@ -312,7 +310,7 @@ def permuter_equipement(self, solution): requested_power > 0 ), "Requested_power should be > 0 because is_waiting is True" - if state and can_change_power and not is_waiting: + elif state and can_change_power and not is_waiting: # change power and accept switching off requested_power = self.calculer_new_power( current_power, power_step, power_min, power_max, True @@ -322,17 +320,17 @@ def permuter_equipement(self, solution): eqt["state"] = False requested_power = 0 - if not state and not is_waiting: + elif not state and not is_waiting: # Allumage eqt["state"] = not state requested_power = power_min - if state and not is_waiting: + elif state and not is_waiting: # Extinction eqt["state"] = not state requested_power = 0 - if "requested_power" not in locals(): + elif "requested_power" not in locals(): _LOGGER.error("We should not be there. eqt=%s", eqt) assert False, "Requested power n'a pas été calculé. Ce n'est pas normal"