Skip to content

Commit

Permalink
Depreciate datapoints per hour (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheFes authored Nov 12, 2023
1 parent 10fefeb commit 295a905
Showing 1 changed file with 41 additions and 64 deletions.
105 changes: 41 additions & 64 deletions cheapest_energy_hours.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,18 @@
{%- endif -%}
{%- endmacro -%}

{%- set n = now().replace(microsecond=0) -%}
{%- set seconds = n.minute * 60 + n.second -%}
{%- macro _div_calc(seconds, hours, datapoint_per_hour, before=false) -%}
{%- macro _div_calc(seconds, hours, dph, before=false) -%}
{%- set div_list = [3600, 1800, 1200, 900, 720, 600, 300] -%}
{%- set ns = namespace(div_reject=[]) -%}
{%- for div in div_list -%}
{%- set check_list = [3600/datapoints_per_hour | default(1), div] | sort -%}
{%- set check_list = [3600/dph | default(1), div] | sort -%}
{%- set check = check_list[1] % check_list[0] == 0 -%}
{%- if not check -%}
{%- set ns.div_reject = ns.div_reject + [div] -%}
{%- endif -%}
{%- endfor -%}
{%- set div_list = div_list | reject('in', ns.div_reject) | list -%}
{%- set ns = namespace(r=3600, wp=1, h=h | default(1)) %}
{%- set ns = namespace(r=3600, wp=1, h=hours | default(1)) %}
{%- for div in div_list %}
{%- if seconds % div == 0 -%}
{%- set remain = 0 -%}
Expand All @@ -33,7 +31,7 @@
{%- if remain < ns.r -%}
{%- set ns.r = remain -%}
{%- set ns.wp = (3600 / div) | int -%}
{%- set ns.h = ((h | default(1) * 3600) / (3600 / ns.wp)) | round(0, 'ceil') * div / 3600 -%}
{%- set ns.h = ((hours * 3600) / (3600 / ns.wp)) | round(0, 'ceil') * div / 3600 -%}
{%- endif -%}
{%- endfor -%}
{{- [ns.wp, ns.h, ns.r] | join('|') -}}
Expand Down Expand Up @@ -111,9 +109,9 @@
program=none,
plot_sensor='sensor.energy_plots',
plot_attr='energy_plots',
datapoints_per_hour=1,
split=false,
debug=false,
datapoints_per_hour='depreciated',
look_ahead_minutes='depreciated'
) -%}
{%- set modes = ['min', 'max', 'average', 'start', 'end', 'list', 'weighted_average','time_min','time_max', 'split', 'all', 'is_now'] -%}
Expand Down Expand Up @@ -143,14 +141,13 @@
program=none,
plot_sensor='sensor.energy_plots',
plot_attr='energy_plots',
split=false,
datapoints_per_hour=1
split=false
)
-%}
{%- set user_input = dict(
sensor=sensor | default(none),
value_on_error= value_on_error | default(none),
hours=hours | default(none),
hours=hours | default(none) | round(3, default=none),
start=start,
end=end,
attr_today=attr_today,
Expand All @@ -171,8 +168,7 @@
program=program,
plot_sensor=plot_sensor,
plot_attr=plot_attr,
split=split,
datapoints_per_hour=datapoints_per_hour
split=split
)
-%}
{%- set non_default = dict(user_input.items() | reject('in', defaults.items())) -%}
Expand All @@ -189,12 +185,27 @@
{# Check if data is available and as expected #}
{%- set today_check = not include_today or today is list and today and today[0] is mapping -%}
{%- set tomorrow_check = not include_tomorrow or tomorrow is list and today and today[0] is mapping -%}
{%- set all_check = not (today_check and tomorrow_check) and all is list and today and today[0] is mapping -%}
{%- set all_check = not (today_check and tomorrow_check) and all is list and all and all[0] is mapping -%}
{# try to find correct attributes if no valid data was found #}
{%- set attr_today = (attr_today if today_check else _find_attr(sensor, 'today')) | default(attr_today, true) -%}
{%- set attr_tomorrow = (attr_tomorrow if tomorrow_check else _find_attr(sensor, 'tomorrow')) | default(attr_tomorrow, true) -%}
{%- set attr_all = (attr_all if all_check else _find_attr(sensor, 'all')) | default(attr_all, true) -%}
{%- set attr_today = (attr_today if today_check else _find_attr(sensor, 'today')) | default('attr_today_not_found', true) -%}
{%- set attr_tomorrow = (attr_tomorrow if tomorrow_check else _find_attr(sensor, 'tomorrow')) | default('attr_tomorrow_not_found', true) -%}
{%- set attr_all = (attr_all if all_check else _find_attr(sensor, 'all')) | default('attr_all_not_found', true) -%}
{# set set dataset with all attributes #}
{%- set today = state_attr(sensor, attr_today) | default([], true) -%}
{%- set tomorrow = state_attr(sensor, attr_tomorrow) | default([], true) -%}
{%- set all = state_attr(sensor, attr_all) | default([], true) -%}
{%- set data = (today + tomorrow) or all -%}
{# Rebuild data from sensor to generic format #}
{%- set rebuild = namespace(data=[]) -%}
{%- for item in data -%}
{%- set time = item[time_key] -%}
{%- set time = as_datetime(time) if time is string else time -%}
{%- set rebuild.data = rebuild.data + [dict(time=time, value=item[value_key] * price_factor | float(1))] -%}
{%- endfor -%}
{%- set data = rebuild.data | sort(attribute='time') -%}
{%- set dph = int(3600 / (data[1].time - data[0].time).total_seconds()) if data | count > 1 else 1 -%}
{# convert start and end input to local datetime #}
{%- set h = hours | default(1) | float(1) -%}
{%- set n = now().replace(microsecond=0) -%}
{# start #}
{%- set day = timedelta(days=1) -%}
Expand All @@ -210,7 +221,7 @@
{%- endif -%}
{%- endif -%}
{%- set seconds = start.minute * 60 + start.second -%}
{%- set div_result = _div_calc(seconds, 1, 1, before=true).split('|') | map('float') | list -%}
{%- set div_result = _div_calc(seconds, h, dph, before=true).split('|') | map('float') | list -%}
{%- set start = start - timedelta(seconds=div_result[2] | int) -%}
{%- set start_dph = div_result[0] -%}
{# end #}
Expand All @@ -224,14 +235,9 @@
{%- set end = end + day if include_tomorrow else end -%}
{%- endif -%}
{%- set seconds = end.minute * 60 + end.second -%}
{%- set div_result = _div_calc(seconds, 1, 1).split('|') | map('float') | list -%}
{%- set div_result = _div_calc(seconds, h, dph).split('|') | map('float') | list -%}
{%- set end = end + timedelta(seconds=div_result[2] | int) -%}
{%- set end_dph = div_result[0] -%}
{# set empty lists if data is not requires becasue of include parameters #}
{%- set today = (state_attr(sensor, attr_today) if start <= today_at() + day) | default([], true) -%}
{%- set tomorrow = (state_attr(sensor, attr_tomorrow) if end >= today_at() + day) | default([], true) -%}
{%- set all = state_attr(sensor, attr_all) | default([], true) -%}
{%- set data = (today + tomorrow) or all -%}
{# Try to find the right time_key and value_key if provided parameters are not found #}
{%- if data[0] | default is mapping -%}
{%- if time_key not in data[0] %}
Expand All @@ -256,7 +262,6 @@
{%- set value_key = vk_list | first if vk_list else value_key -%}
{%- endif -%}
{%- endif -%}
{%- set h = hours | default(1) | float(1) -%}
{%- set use_voe = value_on_error is defined -%}
{# set weight points based on energy plot sensor #}
{%-if program is not none-%}
Expand All @@ -269,29 +274,11 @@
else none
-%}
{# calculate no_weight_points based on hour input #}
{%- set minutes = (h % 1 * 60) | round(0) -%}
{%- set div_list = [60, 30, 20, 15, 12, 10, 5] -%}
{%- set ns = namespace(div_reject=[]) -%}
{%- for div in div_list -%}
{%- set check_list = [60/datapoints_per_hour, div] | sort -%}
{%- set check = check_list[1] % check_list[0] == 0 -%}
{%- if not check -%}
{%- set ns.div_reject = ns.div_reject + [div] -%}
{%- endif -%}
{%- endfor -%}
{%- set div_list = div_list | reject('in', ns.div_reject) | list -%}
{%- set ns = namespace(r=60, wp=1, h=h) %}
{%- for div in div_list %}
{%- set remain = 0 if minutes % div == 0 else div - minutes % div -%}
{%- if remain < ns.r -%}
{%- set ns.r = remain -%}
{%- set ns.wp = (60 / div) | int -%}
{%- set ns.h = ((h * 60) / (60 / ns.wp)) | round(0, 'ceil') * div / 60 -%}
{%- endif -%}{%- set start_dph = div_result[0] -%}
{%- endfor -%}
{%- set h = ns.h -%}
{%- set seconds = (h % 1 * 3600) | round(0) -%}
{%- set div_result = _div_calc(seconds, h, dph).split('|') | map('float') | list -%}
{%- set h_dph, h = div_result[:2] -%}
{# set number of weight points based on all input #}
{%- set no_weight_points = no_weight_points if no_weight_points is not none else [ns.wp, start_dph, end_dph] | max -%}
{%- set no_weight_points = no_weight_points if no_weight_points is not none else [h_dph, start_dph, end_dph] | max -%}
{%- set w = [1] * (h * no_weight_points) | round(0) | int if w is none and no_weight_points is not none else w -%}
{# set number of hours based on weight points in case hours setting is not provided #}
{%- set h_wp = (w | count / no_weight_points) | round(0, 'ceil') | int if w else h -%}
Expand All @@ -310,7 +297,7 @@
{%- if debug -%}
{%- set macro_input = dict(
sensor=sensor,
hours=h,
hours=h | round(3),
start=start.isoformat() if start is datetime else start,
end=end.isoformat() if end is datetime else end,
attr_today=attr_today,
Expand Down Expand Up @@ -348,33 +335,24 @@
{{- dict(non_default_user_input=non_default, set_by_macro=set_by_macro, defaults_used=defaults_used, data_used=data_used, errors=errors) -}}
{# no error - continue with macro #}
{%- else -%}
{# Rebuild data from sensor to generic format #}
{%- set rebuild = namespace(data=[]) -%}
{%- for item in data -%}
{%- set time = item[time_key] -%}
{%- set time = as_datetime(time) if time is string else time -%}
{%- set rebuild.data = rebuild.data + [dict(time=time, value=item[value_key] * price_factor | float(1))] -%}
{%- endfor -%}
{%- set new_data = rebuild.data -%}
{# determine how many data points per hour are used #}
{%- set dph = 3600 / (new_data[1].time - new_data[0].time).seconds -%}
{%- set dp = (h * dph) | round(0, 'ceil') | int -%}
{%- set dp_minutes, wp_minutes = 60 / dph, 60/no_weight_points -%}
{# debugging data #}
{%- if debug -%}
{%- set datapoints_source = dict(datapoins=dp, datapoints_hour=dph) -%}
{%- set datapoints_source = dict(datapoints=dp, datapoints_hour=dph) -%}
{%- endif -%}
{# rebuild data again if weight_points are provided #}
{%- if wp_minutes != dp_minutes and w is not none -%}
{%- if dp_minutes > wp_minutes -%}
{%- set rebuild = namespace(data=[]) -%}
{%- for v in (new_data * int(dp_minutes/wp_minutes)) | sort(attribute='time') -%}
{%- for v in (data * int(dp_minutes/wp_minutes)) | sort(attribute='time') -%}
{%- set t = v.time -%}
{%- set t = t + timedelta(minutes=(wp_minutes*(loop.index0 % int(dp_minutes/wp_minutes)))) -%}
{%- set rebuild.data = rebuild.data + [ dict(time=t, value=v.value) ] -%}
{%- endfor -%}
{%- set new_data = rebuild.data -%}
{%- set dph = 3600 / (new_data[1].time - new_data[0].time).seconds -%}
{%- set data = rebuild.data -%}
{%- set dph = 3600 / (data[1].time - data[0].time).seconds -%}
{%- set dp = (h * dph) | round(0, 'ceil') | int -%}
{%- set dp_minutes, wp_minutes = 60 / dph, 60/no_weight_points -%}
{%- else -%}
Expand All @@ -390,10 +368,10 @@
{%- set check = check_list[1] % check_list[0] == 0 -%}
{# debugging data #}
{%- if debug -%}
{%- set datapoints_used = dict(datapoins=dp, datapoints_hour=dph) -%}
{%- set datapoints_used = dict(datapoints=dp, datapoints_hour=dph) -%}
{%- endif -%}
{# Perform selection based on start and end on the data #}
{%- set values = new_data
{%- set values = data
| selectattr('time', '>=', start)
| selectattr('time', '<', end)
| selectattr('value', 'is_number')
Expand Down Expand Up @@ -470,7 +448,6 @@
{%- set pr = precision | default(5) -%}
{%- set list = split_all | selectattr('prices', 'defined') | map(attribute='prices') | sum(start=[]) -%}
{%- set min, max = list | min, list | max -%}
{%- set index_min, index_max = list.index(min), list.index(max) -%}
{%- set time_min = split_all | selectattr('prices', 'defined') | selectattr('prices', 'contains', min) | map(attribute='start') | join %}
{%- set time_max = split_all | selectattr('prices', 'defined') | selectattr('prices', 'contains', max) | map(attribute='start') | join %}
{%- set is_now = time_max | selectattr('is_now', 'defined') | selectattr('is_now') | list | count > 0 %}
Expand All @@ -487,7 +464,7 @@
is_now = is_now,
all = split_all
)
-%}
-%}
{%- set macro_output = output.all if mode == 'split' else output[mode] -%}
{%- else -%}
{# create output for all modes #}
Expand Down

0 comments on commit 295a905

Please sign in to comment.