Skip to content

Commit

Permalink
Refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Hale committed Mar 29, 2024
1 parent 90ed5df commit a142538
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 49 deletions.
31 changes: 20 additions & 11 deletions src/apps/powerwall/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ def _update_schedules_for_day(day_date):
else:
export_schedules = None

WEEK_SCHEDULES.update(day_date, import_schedules, export_schedules)
weekday = day_date.weekday()
WEEK_SCHEDULES.update(weekday, import_schedules, export_schedules)

return import_schedules, export_schedules

Expand All @@ -171,11 +172,7 @@ def _update_powerwall_tariff():
return

tomorrow = today + tariff.ONE_DAY_INCREMENT
tomorrow_weekday = tomorrow.weekday()
# if tomorrow is the start of a new phase of the week
if tomorrow_weekday == 0 or tomorrow_weekday == 5:
debug("Updating schedules for tomorrow")
_update_schedules_for_day(tomorrow)
_update_schedules_for_day(tomorrow)

import_standing_charge = get_sensor_value("import_standing_charge", 0)
export_standing_charge = get_sensor_value("export_standing_charge", 0)
Expand All @@ -192,15 +189,27 @@ def _update_powerwall_tariff():
debug("Powerwall updated")
status_msg = f"Tariff data updated at {dt.datetime.now()}"
if import_schedules or export_schedules:
status_msg += "("
status_msg += " ("
sep = ""
if import_schedules:
import_breaks = [s.upper_bound for s in import_schedules if s.upper_bound is not None]
status_msg += f"{sep}import breaks: {import_breaks}"
status_msg += f"{sep}import: "
for i, schedule in enumerate(import_schedules):
status_msg += f"{schedule.get_value():.2f}"
if i < len(import_schedules) - 1:
if hasattr(schedule.assigner_func, "upper_bound"):
status_msg += f" |{schedule.assigner_func.upper_bound:.2f}| "
else:
status_msg += "|"
sep = ", "
if export_schedules:
export_breaks = [s.upper_bound for s in export_schedules if s.upper_bound is not None]
status_msg += f"{sep}export breaks: {export_breaks}"
status_msg += f"{sep}export: "
for i, schedule in enumerate(export_schedules):
status_msg += f"{schedule.get_value():.2f}"
if i < len(export_schedules) - 1:
if hasattr(schedule.assigner_func, "upper_bound"):
status_msg += f" |{schedule.assigner_func.upper_bound:.2f}| "
else:
status_msg += "|"
sep = ", "
status_msg += ")"
set_status_message(status_msg)
Expand Down
95 changes: 57 additions & 38 deletions src/modules/powerwall_tariff.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,18 @@ def reset(self):


class Schedule:
def __init__(self, charge_name, lower_bound, upper_bound, pricing_func, pricing_key):
def __init__(self, charge_name, assigner_func, pricing_func, pricing_key):
self.charge_name = charge_name
self.lower_bound = lower_bound
self.upper_bound = upper_bound
self.assigner_func = assigner_func
self.pricing_func = pricing_func
self.pricing_key = pricing_key
self._periods = []
self._value = None
self._start = None
self._end = None

def is_in(self, cost):
return (self.lower_bound is None or cost >= self.lower_bound) and (self.upper_bound is None or cost < self.upper_bound)
def is_in(self, rate):
return self.assigner_func.is_in(rate)

def add(self, rate):
if self._start is None:
Expand Down Expand Up @@ -161,26 +160,15 @@ def get_value(self):

class WeekSchedules:
def __init__(self):
self.midweek_import = None
self.midweek_export = None
self.weekend_import = None
self.weekend_export = None

def update(self, day_date, import_schedules, export_schedules):
weekday = day_date.weekday()
if is_midweek(weekday):
self.midweek_import = import_schedules
self.midweek_export = export_schedules
else:
self.weekend_import = import_schedules
self.weekend_export = export_schedules
self.import_schedules = [None] * 7
self.export_schedules = [None] * 7

def get_schedules(self, day_date):
weekday = day_date.weekday()
if is_midweek(weekday):
return self.midweek_import, self.midweek_export
else:
return self.weekend_import, self.weekend_export
def update(self, weekday, import_schedules, export_schedules):
self.import_schedules[weekday] = import_schedules
self.export_schedules[weekday] = export_schedules

def get_schedules(self, weekday, export=False):
return self.export_schedules[weekday] if export else self.import_schedules[weekday]


def lowest_rates(rates, hrs):
Expand All @@ -205,7 +193,17 @@ def highest_rates(rates, hrs):
}


class AveragePricing():
class PriceBandAssigner:
def __init__(self, lower_bound, upper_bound):
self.lower_bound = lower_bound
self.upper_bound = upper_bound

def is_in(self, rate):
cost = rate[PRICE_KEY]
return (self.lower_bound is None or cost >= self.lower_bound) and (self.upper_bound is None or cost < self.upper_bound)


class AveragePricing:
def __init__(self):
self.sum = 0
self.count = 0
Expand All @@ -224,7 +222,7 @@ def get_value(self):
return 0.0


class MinimumPricing():
class MinimumPricing:
def __init__(self):
self.min = PRICE_CAP

Expand All @@ -239,7 +237,7 @@ def get_value(self):
return v


class MaximumPricing():
class MaximumPricing:
def __init__(self):
self.max = 0

Expand Down Expand Up @@ -305,12 +303,21 @@ def get_breaks(break_config, rates):
return breaks


def get_tariff_assigners(break_config, rates):
breaks = get_breaks(break_config, rates)
funcs = []
for i in range(len(breaks)+1):
lower_bound = breaks[i-1] if i > 0 else None
upper_bound = breaks[i] if i < len(breaks) else None
funcs.append(PriceBandAssigner(lower_bound, upper_bound))
return funcs


def populate_schedules(schedules, day_rates):
for rate in day_rates:
cost = rate[PRICE_KEY]
schedule = None
for s in schedules:
if s.is_in(cost):
if s.is_in(rate):
schedule = s
break
schedule.add(rate)
Expand All @@ -331,14 +338,12 @@ def get_schedules(breaks_config, plunge_pricing_breaks_config, tariff_pricing_co
else:
configured_breaks = breaks_config

breaks = get_breaks(configured_breaks, day_rates)
assigner_funcs = get_tariff_assigners(configured_breaks, day_rates)

schedules = []
for i, charge_name in enumerate(CHARGE_NAMES):
lower_bound = breaks[i-1] if i > 0 else None
upper_bound = breaks[i] if i < len(breaks) else None
pricing_func = create_pricing(tariff_pricing_config[i])
schedules.append(Schedule(charge_name, lower_bound, upper_bound, pricing_func, pricing_key))
schedules.append(Schedule(charge_name, assigner_funcs[i], pricing_func, pricing_key))

populate_schedules(schedules, day_rates)

Expand All @@ -365,9 +370,21 @@ def populate_tou_periods(tou_periods, schedules, start_day_of_week, end_day_of_w
charge_periods.append(to_charge_period_json(start_day_of_week, end_day_of_week, period))


def schedules_to_tariff(midweek_schedules, weekend_schedules):
def schedules_to_tariff(week_schedules, export=False):
tou_periods = {charge_name: [] for charge_name in CHARGE_NAMES}

midweek_schedules = None
for i in range(4, -1, -1):
midweek_schedules = week_schedules.get_schedules(i, export)
if midweek_schedules:
break

weekend_schedules = None
for i in range(6, 4, -1):
weekend_schedules = week_schedules.get_schedules(i, export)
if weekend_schedules:
break

if midweek_schedules and weekend_schedules:
populate_tou_periods(tou_periods, midweek_schedules, 0, 4)
populate_tou_periods(tou_periods, midweek_schedules, 5, 6)
Expand All @@ -392,12 +409,14 @@ def get_price_info(schedules):


def to_tariff_data(config, import_standing_charge, export_standing_charge, week_schedules, day_date):
current_import_schedules, current_export_schedules = week_schedules.get_schedules(day_date)
import_seasons = schedules_to_tariff(week_schedules.midweek_import, week_schedules.weekend_import)
weekday = day_date.weekday()
current_import_schedules = week_schedules.get_schedules(weekday)
current_export_schedules = week_schedules.get_schedules(weekday, export=True)
import_seasons = schedules_to_tariff(week_schedules)
buy_price_info = get_price_info(current_import_schedules);

if week_schedules.midweek_export or week_schedules.weekend_export:
export_seasons = schedules_to_tariff(week_schedules.midweek_export, week_schedules.weekend_export)
if current_export_schedules:
export_seasons = schedules_to_tariff(week_schedules, export=True)
sell_price_info = get_price_info(current_export_schedules);
else:
export_seasons = import_seasons
Expand Down

0 comments on commit a142538

Please sign in to comment.