summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/home/inverter/__init__.py3
-rw-r--r--src/home/inverter/monitor.py45
-rwxr-xr-xsrc/inverter_bot.py42
3 files changed, 88 insertions, 2 deletions
diff --git a/src/home/inverter/__init__.py b/src/home/inverter/__init__.py
index 374bc7b..967ddff 100644
--- a/src/home/inverter/__init__.py
+++ b/src/home/inverter/__init__.py
@@ -3,7 +3,8 @@ from .monitor import (
InverterMonitor,
BatteryState,
BatteryPowerDirection,
- ACMode
+ ACMode,
+ ACPresentEvent
)
from .inverter_wrapper import wrapper_instance
from .util import beautify_table
diff --git a/src/home/inverter/monitor.py b/src/home/inverter/monitor.py
index 87e07be..edfd8f7 100644
--- a/src/home/inverter/monitor.py
+++ b/src/home/inverter/monitor.py
@@ -27,6 +27,15 @@ class ChargingEvent(Enum):
AC_MOSTLY_CHARGED = auto()
AC_CHARGING_FINISHED = auto()
+ UTIL_CHARGING_STARTED = auto()
+ UTIL_CHARGING_STOPPED = auto()
+ UTIL_CHARGING_STOPPED_SOLAR = auto()
+
+
+class ACPresentEvent(Enum):
+ CONNECTED = auto()
+ DISCONNECTED = auto()
+
class ChargingState(Enum):
NOT_CHARGING = auto()
@@ -83,6 +92,7 @@ TODO:
class InverterMonitor(Thread):
charging_event_handler: Optional[Callable]
battery_event_handler: Optional[Callable]
+ util_event_handler: Optional[Callable]
error_handler: Optional[Callable]
def __init__(self):
@@ -96,6 +106,7 @@ class InverterMonitor(Thread):
# Event handlers for the bot.
self.charging_event_handler = None
self.battery_event_handler = None
+ self.util_event_handler = None
self.error_handler = None
# Currents list, defined in the bot config.
@@ -120,6 +131,11 @@ class InverterMonitor(Thread):
# We don't want to damage our batteries, right?
self.floating_stopwatch = Stopwatch()
+ # State variables for utilities charging program
+ self.util_ac_present = None
+ self.util_pd = None
+ self.util_solar = None
+
@property
def active_current(self) -> Optional[int]:
try:
@@ -159,6 +175,8 @@ class InverterMonitor(Thread):
if self.ac_mode == ACMode.GENERATOR:
self.gen_charging_program(ac, solar, v, pd)
+ elif self.ac_mode == ACMode.UTILITIES:
+ self.utilities_monitoring_program(ac, solar, pd)
if not ac or pd != BatteryPowerDirection.CHARGING:
# if AC is disconnected or not charging, run the low voltage checking program
@@ -173,6 +191,30 @@ class InverterMonitor(Thread):
time.sleep(2)
+ def utilities_monitoring_program(self,
+ ac: bool, # whether AC is connected
+ solar: bool, # whether MPPT is active
+ pd: BatteryPowerDirection # current power direction
+ ):
+ pd_event_send = False
+ if self.util_solar is None or solar != self.util_solar:
+ self.util_solar = solar
+ if solar and self.util_ac_present and self.util_pd == BatteryPowerDirection.CHARGING:
+ self.charging_event_handler(ChargingEvent.UTIL_CHARGING_STOPPED_SOLAR)
+ pd_event_send = True
+
+ if self.util_ac_present is None or ac != self.util_ac_present:
+ self.util_event_handler(ACPresentEvent.CONNECTED if ac else ACPresentEvent.DISCONNECTED)
+ self.util_ac_present = ac
+
+ if self.util_pd is None or self.util_pd != pd:
+ if not pd_event_send:
+ if pd == BatteryPowerDirection.CHARGING:
+ self.charging_event_handler(ChargingEvent.UTIL_CHARGING_STARTED)
+ else:
+ self.charging_event_handler(ChargingEvent.UTIL_CHARGING_STOPPED)
+ self.util_pd = pd
+
def gen_charging_program(self,
ac: bool, # whether AC is connected
solar: bool, # whether MPPT is active
@@ -444,6 +486,9 @@ class InverterMonitor(Thread):
def set_battery_event_handler(self, handler: Callable):
self.battery_event_handler = handler
+ def set_util_event_handler(self, handler: Callable):
+ self.util_event_handler = handler
+
def set_error_handler(self, handler: Callable):
self.error_handler = handler
diff --git a/src/inverter_bot.py b/src/inverter_bot.py
index 54b17c8..c647115 100755
--- a/src/inverter_bot.py
+++ b/src/inverter_bot.py
@@ -22,6 +22,7 @@ from home.inverter import (
InverterMonitor,
ChargingEvent,
+ ACPresentEvent,
BatteryState,
ACMode
)
@@ -55,6 +56,7 @@ SETACMODE_STARTED, = range(1)
def monitor_charging(event: ChargingEvent, **kwargs) -> None:
args = []
+ is_util = False
if event == ChargingEvent.AC_CHARGING_STARTED:
key = 'started'
elif event == ChargingEvent.AC_CHARGING_FINISHED:
@@ -70,12 +72,24 @@ def monitor_charging(event: ChargingEvent, **kwargs) -> None:
key = 'na_solar'
elif event == ChargingEvent.AC_MOSTLY_CHARGED:
key = 'mostly_charged'
+ elif event == ChargingEvent.UTIL_CHARGING_STOPPED:
+ key = 'started'
+ is_util = True
+ elif event == ChargingEvent.UTIL_CHARGING_STOPPED:
+ key = 'stopped'
+ is_util = True
+ elif event == ChargingEvent.UTIL_CHARGING_STOPPED_SOLAR:
+ key = 'stopped_solar'
+ is_util = True
else:
logger.error('unknown charging event:', event)
return
+ key = f'chrg_evt_{key}'
+ if is_util:
+ key = f'util_{key}'
bot.notify_all(
- lambda lang: bot.lang.get(f'chrg_evt_{key}', lang, *args)
+ lambda lang: bot.lang.get(key, lang, *args)
)
@@ -96,6 +110,17 @@ def monitor_battery(state: BatteryState, v: float, load_watts: int) -> None:
)
+def monitor_util(event: ACPresentEvent):
+ if event == ACPresentEvent.CONNECTED:
+ key = 'connected'
+ else:
+ key = 'disconnected'
+ key = f'util_{key}'
+ bot.notify_all(
+ lambda lang: bot.lang.get(key, lang)
+ )
+
+
def monitor_error(error: str) -> None:
bot.notify_all(
lambda lang: bot.lang.get('error_message', lang, error)
@@ -443,6 +468,13 @@ class InverterBot(Wrapper):
battery_level_changed='Уровень заряда АКБ: <b>%s %s</b> (<b>%0.1f V</b> при нагрузке <b>%d W</b>)',
error_message='<b>Ошибка:</b> %s.',
+ util_chrg_evt_started='✅ Начали заряжать от столба.',
+ util_chrg_evt_stopped='ℹ️ Перестали заряжать от столба.',
+ util_chrg_evt_stopped_solar='ℹ️ Перестали заряжать от столба из-за подключения панелей.',
+
+ util_connected='✅️ Столб подключен.',
+ util_disconnected='‼️ Столб отключён.',
+
# other notifications
ac_mode_changed_notification='Пользователь <a href="tg://user?id=%d">%s</a> установил режим AC: <b>%s</b>.',
@@ -506,6 +538,13 @@ class InverterBot(Wrapper):
battery_level_changed='Battery level: <b>%s</b> (<b>%0.1f V</b> under <b>%d W</b> load)',
error_message='<b>Error:</b> %s.',
+ util_chrg_evt_started='✅ Started charging from utilities.',
+ util_chrg_evt_stopped='ℹ️ Stopped charging from utilities.',
+ util_chrg_evt_stopped_solar='ℹ️ Stopped charging from utilities because solar panels were connected.',
+
+ util_connected='✅️ Utilities connected.',
+ util_disconnected='‼️ Utilities disconected.',
+
# other notifications
ac_mode_changed_notification='User <a href="tg://user?id=%d">%s</a> set AC mode to <b>%s</b>.',
@@ -603,6 +642,7 @@ if __name__ == '__main__':
monitor = InverterMonitor()
monitor.set_charging_event_handler(monitor_charging)
monitor.set_battery_event_handler(monitor_battery)
+ monitor.set_util_event_handler(monitor_util)
monitor.set_error_handler(monitor_error)
monitor.start()