Skip to content

Commit

Permalink
#89 Notifications from python instead of HA automations. (#91)
Browse files Browse the repository at this point in the history
* #89 Notifications from python instead of HA automations.
No longer fixed % in min SoC message.
Preparation for setting for max. SoC.

Includes a small fix for better initialisation flexmeasures_client en some spelling errors.

* #89 Notifications from python instead of HA automations.
Processed comments
  • Loading branch information
ArdJonker committed Feb 9, 2023
1 parent 44a863f commit 4f23b4b
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 61 deletions.
46 changes: 1 addition & 45 deletions app_config/v2g_liberty_package.yaml
Expand Up @@ -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
6 changes: 3 additions & 3 deletions flexmeasures_client.py
Expand Up @@ -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/" + \
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions get_fm_data.py
Expand Up @@ -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
"""
Expand Down
30 changes: 18 additions & 12 deletions v2g_liberty.py
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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):
"""
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion wallbox_client.py
Expand Up @@ -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.
Expand Down

0 comments on commit 4f23b4b

Please sign in to comment.