Skip to content

Commit

Permalink
Add carbon intensity support for Inky display
Browse files Browse the repository at this point in the history
  • Loading branch information
jerbzz committed Aug 9, 2021
1 parent 548b654 commit 97d31bf
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 44 deletions.
2 changes: 1 addition & 1 deletion config.yaml.default
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
DNORegion: B
DNORegion: Z
# Permitted regions are:
# A = East England
# B = East Midlands
Expand Down
95 changes: 54 additions & 41 deletions eco_indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,15 @@ def update_blinkt(conf: dict, blinkt_data: dict, demo: bool):
blinkt.set_clear_on_exit(False)
blinkt.show()

def update_inky(conf: dict, prices: dict, demo: bool):
"""Recieve a parsed configuration file and price data from the database,
as well as a flag indicating demo mode, and then update the Blinkt!
def update_inky(conf: dict, inky_data: dict, demo: bool):
"""Recieve a parsed configuration file and price/carbon data from the database,
as well as a flag indicating demo mode, and then update the Inky
display appropriately.
Notes: list 'prices' as passed from update_display.py is an ordered
Notes: list 'inky_data' as passed from update_display.py is an ordered
list of tuples. In each tuple, index [0] is the time in SQLite date
format and index [1] is the price in p/kWh as a float."""
format and index [1] is the price in p/kWh as a float. index [2] is
the carbon intensity as an integer."""
local_tz = get_localzone()

try:
Expand Down Expand Up @@ -103,7 +104,19 @@ def update_inky(conf: dict, prices: dict, demo: bool):
y_padding_factor = 1

if conf['Mode'] == "carbon":
raise SystemExit("Carbon mode not yet implemented for Inky display.")
tuple_idx = 2
short_unit = "g"
descriptor = "Carbon at "
high_value = conf['InkyPHAT']['HighIntensity']
format_str = "{:.0f}"
graph_y_unit = graph_y_unit / 15

if conf['Mode'] == "agile_price":
tuple_idx = 1
short_unit = "p"
descriptor = "Price from "
high_value = conf['InkyPHAT']['HighPrice']
format_str = "{0:.1f}"

if demo:
print ("Demo mode... (not implemented!)")
Expand All @@ -112,53 +125,53 @@ def update_inky(conf: dict, prices: dict, demo: bool):
# figure out cheapest slots
low_slot_duration = conf['InkyPHAT']['LowSlotDuration']
num_low_slots = int(2 * low_slot_duration)
prices_only = [price[1] for price in prices]
inky_data_only = [slot_data[tuple_idx] for slot_data in inky_data]
low_slots_list = []
for i in range(0, len(prices_only) - num_low_slots - 1):
low_slots_list.append(sum(prices_only[i:i+num_low_slots])/num_low_slots)
for i in range(0, len(inky_data_only) - num_low_slots - 1):
low_slots_list.append(sum(inky_data_only[i:i+num_low_slots])/num_low_slots)
low_slots_start_idx = low_slots_list.index(min(low_slots_list))
low_slots_average = "{0:.1f}".format(min(low_slots_list))
low_slots_average = format_str.format(min(low_slots_list))

low_slots_start_time = str(datetime.strftime(pytz.utc.localize(
datetime.strptime(prices[low_slots_start_idx][0],
datetime.strptime(inky_data[low_slots_start_idx][0],
"%Y-%m-%d %H:%M:%S"),
is_dst=None).astimezone(local_tz), "%H:%M"))
print("Cheapest " + str(low_slot_duration) + " hours: average " +
low_slots_average + "p/kWh at " + low_slots_start_time + ".")
print("Lowest " + str(low_slot_duration) + " hours: average " +
low_slots_average + short_unit + "/kWh at " + low_slots_start_time + ".")

min_slot = min(prices, key = lambda prices: prices[1])
min_slot_price = str(min_slot[1])
min_slot = min(inky_data, key = lambda inky_data: inky_data[tuple_idx])
min_slot_value = str(min_slot[tuple_idx])
min_slot_time = str(datetime.strftime(pytz.utc.localize(datetime.strptime(min_slot[0],
"%Y-%m-%d %H:%M:%S"),
is_dst=None).astimezone(local_tz), "%H:%M"))

print("Lowest priced slot: " + min_slot_price + "p at " + min_slot_time + ".")
print("Lowest value slot: " + min_slot_value + short_unit + " at " + min_slot_time + ".")

# figure out the cheapest slot

# draw graph solid bars...
# shift axis for negative prices
if min_slot[1] < 0:
if min_slot[tuple_idx] < 0:
graph_bottom = (inky_display.HEIGHT + min_slot[1]
* graph_y_unit) - 13 * y_padding_factor
else:
graph_bottom = inky_display.HEIGHT - 13 * y_padding_factor

i = 0
for price in prices:
for slot_data in inky_data:
# draw the lowest slots in black and the highest in red/yellow

if (i + 1) * graph_x_unit > 127 * x_padding_factor:
break # don't scribble on the small text

if low_slots_start_idx <= i < low_slots_start_idx + num_low_slots:
colour = inky_display.BLACK
elif price[1] > conf['InkyPHAT']['HighPrice']:
elif slot_data[tuple_idx] > high_value:
colour = inky_display.RED
else:
colour = inky_display.WHITE

bar_y_height = price[1] * graph_y_unit
bar_y_height = slot_data[tuple_idx] * graph_y_unit

draw.rectangle(((i + 1) * graph_x_unit, graph_bottom,
(((i + 1) * graph_x_unit) - graph_x_unit),
Expand All @@ -169,31 +182,31 @@ def update_inky(conf: dict, prices: dict, demo: bool):
# draw current price, in colour if it's high...
# also highlight display with a coloured border if current price is high
font = ImageFont.truetype(RobotoBlack, size = int(45 * font_scale_factor))
message = "{0:.1f}".format(prices[0][1]) + "p"
message = format_str.format(inky_data[0][tuple_idx]) + short_unit
x_pos = 0 * x_padding_factor
y_pos = 8 * y_padding_factor

slot_start = str(datetime.strftime(pytz.utc.localize(datetime.strptime(prices[0][0],
slot_start = str(datetime.strftime(pytz.utc.localize(datetime.strptime(inky_data[0][0],
"%Y-%m-%d %H:%M:%S"), is_dst=None).astimezone(local_tz), "%H:%M"))

if prices[0][1] > conf['InkyPHAT']['HighPrice']:
if inky_data[0][tuple_idx] > high_value:
draw.text((x_pos, y_pos), message, inky_display.RED, font)
inky_display.set_border(inky_display.RED)
print("Current price from " + slot_start + ": " + message + " (High)")
print("Current value from " + slot_start + ": " + message + " (High)")
else:
draw.text((x_pos, y_pos), message, inky_display.BLACK, font)
inky_display.set_border(inky_display.WHITE)
print("Current price from " + slot_start + ": " + message)
print("Current value from " + slot_start + ": " + message)

# draw time info above current price...
font = ImageFont.truetype(RobotoMedium, size = int(15 * font_scale_factor))
message = "Price from " + slot_start + " " # trailing spaces prevent text clipping
message = descriptor + slot_start + " " # trailing spaces prevent text clipping
x_pos = 4 * x_padding_factor
y_pos = 0 * y_padding_factor
draw.text((x_pos, y_pos), message, inky_display.BLACK, font)

mins_until_next_slot = ceil((pytz.utc.localize(datetime.strptime(
prices[1][0], "%Y-%m-%d %H:%M:%S"), is_dst=None) - datetime.now(
inky_data[1][0], "%Y-%m-%d %H:%M:%S"), is_dst=None) - datetime.now(
pytz.timezone("UTC"))).total_seconds() / 60)

print(str(mins_until_next_slot) + " mins until next slot.")
Expand All @@ -210,10 +223,10 @@ def update_inky(conf: dict, prices: dict, demo: bool):
# draw next 3 slot prices...
x_pos = 163 * x_padding_factor
for i in range(3):
message = "{0:.1f}".format(prices[i+1][1]) + "p "
message = format_str.format(inky_data[i+1][tuple_idx]) + short_unit + " "
# trailing spaces prevent text clipping
y_pos = i * 18 * y_padding_factor + 3 * y_padding_factor
if prices[i+1][1] > conf['InkyPHAT']['HighPrice']:
if inky_data[i+1][tuple_idx] > high_value:
draw.text((x_pos, y_pos), message, inky_display.RED, font)
else:
draw.text((x_pos, y_pos), message, inky_display.BLACK, font)
Expand All @@ -233,24 +246,24 @@ def update_inky(conf: dict, prices: dict, demo: bool):
else:
lsd_text = str(low_slot_duration)

draw.text((x_pos, y_pos), lsd_text + "h @" + low_slots_average + "p ",
draw.text((x_pos, y_pos), lsd_text + "h @" + low_slots_average + short_unit + " ",
inky_display.BLACK, font)

y_pos = 16 * (y_padding_factor * 0.6) + (4 * 18 * y_padding_factor)

min_slot_timedelta = datetime.strptime(prices[low_slots_start_idx][0],
min_slot_timedelta = datetime.strptime(inky_data[low_slots_start_idx][0],
"%Y-%m-%d %H:%M:%S") - datetime.strptime(
prices[0][0], "%Y-%m-%d %H:%M:%S")
inky_data[0][0], "%Y-%m-%d %H:%M:%S")
draw.text((x_pos, y_pos), low_slots_start_time + "/" +
str(min_slot_timedelta.total_seconds() / 3600) +
"h ", inky_display.BLACK, font)

# draw graph outline (last so it's over the top of everything else)
i = 0
for i, price in enumerate(prices):
for i, slot_data in enumerate(inky_data):
colour = inky_display.BLACK
bar_y_height = price[1] * graph_y_unit
prev_bar_y_height = prices[i-1][1] * graph_y_unit
bar_y_height = slot_data[tuple_idx] * graph_y_unit
prev_bar_y_height = inky_data[i-1][tuple_idx] * graph_y_unit

if (i + 1) * graph_x_unit > 127 * x_padding_factor: # don't scribble on the small text
break
Expand All @@ -275,7 +288,7 @@ def update_inky(conf: dict, prices: dict, demo: bool):
for i in range(2, 24, 3):
colour = inky_display.BLACK
font = ImageFont.truetype(RobotoMedium, size = int(10 * font_scale_factor))
x_pos = (i - 0.5) * graph_x_unit * 2 # it's half hour slots!!
x_pos = i * graph_x_unit * 2 # it's half hour slots!!
hours = datetime.strftime(datetime.now() + timedelta(hours=i),"%H")
hours_w, hours_h = font.getsize(hours) # we want to centre the labels
y_pos = graph_bottom + 1
Expand All @@ -287,14 +300,14 @@ def update_inky(conf: dict, prices: dict, demo: bool):
inky_display.BLACK)

# draw average line...
# extract just prices from the list of tuples and put in descending order
price_list = sorted(list(zip(*prices))[1], reverse=True)
# extract just values from the list of tuples and put in descending order
slot_data_list = sorted(list(zip(*inky_data))[tuple_idx], reverse=True)
# now slice off the first (highest) 6 entries
del price_list[:6]
del slot_data_list[:6]
# and calculate the mean
average_price = sum(price_list) / len(price_list)
average_slot_data = sum(slot_data_list) / len(slot_data_list)

average_line_ypos = graph_bottom - average_price * graph_y_unit
average_line_ypos = graph_bottom - average_slot_data * graph_y_unit

for x_pos in range (0, int(126 * x_padding_factor)):
if x_pos % 6 == 2: # repeat every 6 pixels starting at 2
Expand Down
4 changes: 2 additions & 2 deletions store_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def insert_record(valid_from: str, data_value: float) -> bool:
datetime.strptime(valid_from, "%Y-%m-%dT%H:%M:%SZ"), "%Y-%m-%d %H:%M:%S")

data_tuple = (valid_from_formatted, data_value)
print(data_tuple) # debug
# print(data_tuple) # debug

try:
cursor.execute(
Expand All @@ -171,7 +171,7 @@ def insert_record(valid_from: str, data_value: float) -> bool:
datetime.strptime(valid_from, "%Y-%m-%dT%H:%MZ"), "%Y-%m-%d %H:%M:%S")

data_tuple = (valid_from_formatted, data_value)
print(data_tuple) # debug
# print(data_tuple) # debug

try:
cursor.execute(
Expand Down

0 comments on commit 97d31bf

Please sign in to comment.