diff --git a/app_config/v2g_liberty_package.yaml b/app_config/v2g_liberty_package.yaml index 79859ca..499828a 100755 --- a/app_config/v2g_liberty_package.yaml +++ b/app_config/v2g_liberty_package.yaml @@ -499,48 +499,4 @@ automation: data: value: Discharging default: [] - mode: single - - id: "1630080858311" - alias: Notify for Car SoC > 80% - description: "" - trigger: - - platform: numeric_state - entity_id: input_number.car_state_of_charge - above: "80" - condition: [] - action: - - device_id: d316527a7a32664c800e47c500c9c3f2 - domain: mobile_app - type: notify - title: Car battery state of charge is above 80% - message: Car battery state of charge is above 80% - mode: single - - id: "1635947176767" - alias: Notify for car SoC < 20% - description: "" - trigger: - - platform: numeric_state - entity_id: input_number.car_state_of_charge - below: "20" - condition: [] - action: - - device_id: d316527a7a32664c800e47c500c9c3f2 - domain: mobile_app - type: notify - title: Car battery state of charge is below 20% - message: Car battery state of charge is below 20%, Max boost has been activated. - mode: single - - id: "1638475858743" - alias: Notify for failing to get EPEX prices for tomorrow - description: "TODO: Replace by direct notification from Python code.." - trigger: - - platform: state - entity_id: input_text.epex_log - condition: [] - action: - - device_id: d316527a7a32664c800e47c500c9c3f2 - domain: mobile_app - type: notify - message: Failed to get EPEX prices for tomorrow - title: Failed to get EPEX prices for tomorrow - mode: single + mode: single \ No newline at end of file diff --git a/flexmeasures_client.py b/flexmeasures_client.py index dc8e868..13d6023 100755 --- a/flexmeasures_client.py +++ b/flexmeasures_client.py @@ -42,6 +42,9 @@ class FlexMeasuresClient(hass.Hass): previous_trigger_message: str def initialize(self): + self.previous_trigger_message = "" + self.fm_busy_getting_schedule = False + self.FM_API = self.args["fm_api"] self.FM_URL = self.FM_API + "/" + \ self.args["fm_api_version"] + "/sensors/" + \ @@ -71,9 +74,6 @@ def initialize(self): self.WALLBOX_PLUS_CAR_ROUNDTRIP_EFFICIENCY = float(self.args["wallbox_plus_car_roundtrip_efficiency"]) - self.previous_trigger_message = "" - self.fm_busy_getting_schedule = False - def authenticate_with_fm(self): """Authenticate with the FlexMeasures server and store the returned auth token. diff --git a/get_fm_data.py b/get_fm_data.py index 2d9e6da..296d40f 100755 --- a/get_fm_data.py +++ b/get_fm_data.py @@ -48,6 +48,7 @@ def initialize(self): self.log(f"Done setting up get_fm_data: check daily at {self.first_try_time_price_data} for new data with FM.") + # ToDo: Make generic function in utils? See v2g_liberty.py for equivalent. def notify_user(self, message: str): """ Utility function to notify the user """ diff --git a/v2g_liberty.py b/v2g_liberty.py index a19a302..4473781 100755 --- a/v2g_liberty.py +++ b/v2g_liberty.py @@ -2,7 +2,7 @@ from itertools import accumulate import time import pytz -from typing import AsyncGenerator, List +from typing import AsyncGenerator, List, Optional import appdaemon.plugins.hass.hassapi as hass import isodate @@ -114,7 +114,7 @@ def disconnect_charger(self, *args, **kwargs): self.set_charger_action("stop") self.set_charger_control("give") # ToDo: Remove all schedules? - self.notify_user("Charger disconnected charger.") + self.notify_user("Charger is disconnected.") def restart_charger(self, *args, **kwargs): """ Function to (forcefully) restart the charger. @@ -124,29 +124,34 @@ def restart_charger(self, *args, **kwargs): self.set_charger_action("restart") self.notify_user("Restart of charger initiated by user. Please check charger.") - # TODO: combine with same function in other modules?? - def notify_user(self, message: str, critical=False): + # ToDo: Make generic function in utils? See get_fm_data.py for equivalent. + def notify_user(self, message: str, critical: bool = False, title: Optional[str] = None): """ Utility function to send notifications to the user via HA""" - self.log(f"Notify_user a:{self.ADMIN_MOBILE_NAME}, b:{self.ADMIN_MOBILE_PLATFORM}") + self.log(f"Notify device '{self.ADMIN_MOBILE_NAME}' on platform '{self.ADMIN_MOBILE_PLATFORM}' " + f"with message'{message}'.") if self.ADMIN_MOBILE_NAME is None or self.ADMIN_MOBILE_NAME == "": # If no device to send to then follow normal flow. critical = False + if title: + title = "V2G Liberty: " + title + else: + title = "V2G Liberty" if critical: device_address = "notify/mobile_app_" + self.ADMIN_MOBILE_NAME if self.ADMIN_MOBILE_PLATFORM == "ios": self.call_service(device_address, - title="V2G Liberty", + title=title, message=message, data={"push": {"sound": {"critical": 1, "name": "default", "volume": 0.9}}}) elif self.ADMIN_MOBILE_PLATFORM == "android": self.call_service(device_address, - title="V2G Liberty", + title=title, message=message, data={"ttl": 0, "priority": "high"}) else: - self.notify(message, title="V2G Liberty") + self.notify(message, title=title) def decide_whether_to_ask_for_new_schedule(self): """ @@ -317,15 +322,16 @@ def set_next_action(self): # Intended for the situation where the car returns from a trip with a low battery. # An SoC below the minimum SoC is considered "unhealthy" for the battery, # this is why the battery should be charged to this minimum asap. - - self.log(f"Starting max charge now and not requesting schedule based on SoC below" - f" minimum ({self.CAR_MIN_SOC_IN_PERCENT}%).") + message = f"Car battery state of charge ({self.connected_car_soc}%) is too low. " \ + f"Charging with maximum power until minimum of ({self.CAR_MIN_SOC_IN_PERCENT}%) is reached." # Cancel previous scheduling timers as they might have discharging instructions as well self.cancel_charging_timers() self.start_max_charge_now() + self.notify_user(message, False, "Car battery is too low") self.in_boost_to_reach_min_soc = True return - elif self.connected_car_soc > self.CAR_MIN_SOC_IN_PERCENT and self.in_boost_to_reach_min_soc: + + if self.connected_car_soc > self.CAR_MIN_SOC_IN_PERCENT and self.in_boost_to_reach_min_soc: self.log(f"Stopping max charge now, SoC above minimum ({self.CAR_MIN_SOC_IN_PERCENT}%) again.") self.in_boost_to_reach_min_soc = False self.set_power_setpoint(0) diff --git a/wallbox_client.py b/wallbox_client.py index 8f40a59..1b189b0 100755 --- a/wallbox_client.py +++ b/wallbox_client.py @@ -403,12 +403,19 @@ def process_soc(self, reported_soc: str) -> bool: self.connected_car_soc = round(reported_soc, 0) self.connected_car_soc_kwh = round(reported_soc * float(self.CAR_MAX_CAPACITY_IN_KWH / 100), 2) self.log(f"New SoC processed, self.connected_car_soc is now set to: {self.connected_car_soc}%.") + + # Notify user of reaching 80% charge while charging (not dis-charging). + # ToDo: Discuss with users if this is useful. + # ToDo: Replace 80 with max soc setting from yaml. + notify_soc_setting = 80 + if self.connected_car_soc == notify_soc_setting and self.is_charging(): + self.notify_user(f"Car battery state of charge has reached {notify_soc_setting}%.") return True def handle_charger_in_error(self): # If charger remains in error state more than 7 minutes restart the charger. # We wait 7 minutes as the charger might be return an error state up until 5 minutes after a restart. - # The default charger_in_error_since is filled with the refrence date. + # The default charger_in_error_since is filled with the reference date. # At the registration of an error charger_in_error_since is set to now. # This way we know "checking for error" is in progress if the charger_in_error_since is # not equal to the reference date.