emhass 0.8.4__py3-none-any.whl → 0.8.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
emhass/command_line.py CHANGED
@@ -59,8 +59,8 @@ def set_input_data_dict(config_path: pathlib.Path, base_path: str, costfun: str,
59
59
  optim_conf, plant_conf, set_type, logger)
60
60
  # Define main objects
61
61
  rh = RetrieveHass(retrieve_hass_conf['hass_url'], retrieve_hass_conf['long_lived_token'],
62
- retrieve_hass_conf['freq'], retrieve_hass_conf['time_zone'],
63
- params, base_path, logger, get_data_from_file=get_data_from_file)
62
+ retrieve_hass_conf['freq'], retrieve_hass_conf['time_zone'],
63
+ params, base_path, logger, get_data_from_file=get_data_from_file)
64
64
  fcst = Forecast(retrieve_hass_conf, optim_conf, plant_conf,
65
65
  params, base_path, logger, get_data_from_file=get_data_from_file)
66
66
  opt = Optimization(retrieve_hass_conf, optim_conf, plant_conf,
Binary file
Binary file
emhass/forecast.py CHANGED
@@ -7,6 +7,8 @@ import copy
7
7
  import logging
8
8
  import json
9
9
  from typing import Optional
10
+ import bz2
11
+ import pickle as cPickle
10
12
  import pandas as pd
11
13
  import numpy as np
12
14
  from datetime import datetime, timedelta
@@ -21,7 +23,7 @@ from pvlib.irradiance import disc
21
23
 
22
24
  from emhass.retrieve_hass import RetrieveHass
23
25
  from emhass.machine_learning_forecaster import MLForecaster
24
- from emhass.utils import get_days_list
26
+ from emhass.utils import get_days_list, get_root
25
27
 
26
28
 
27
29
  class Forecast(object):
@@ -133,7 +135,7 @@ class Forecast(object):
133
135
  self.time_zone = self.retrieve_hass_conf['time_zone']
134
136
  self.method_ts_round = self.retrieve_hass_conf['method_ts_round']
135
137
  self.timeStep = self.freq.seconds/3600 # in hours
136
- self.time_delta = pd.to_timedelta(opt_time_delta, "hours") # The period of optimization
138
+ self.time_delta = pd.to_timedelta(opt_time_delta, "hours")
137
139
  self.var_PV = self.retrieve_hass_conf['var_PV']
138
140
  self.var_load = self.retrieve_hass_conf['var_load']
139
141
  self.var_load_new = self.var_load+'_positive'
@@ -159,7 +161,7 @@ class Forecast(object):
159
161
  self.end_forecast = (self.start_forecast + self.optim_conf['delta_forecast']).replace(microsecond=0)
160
162
  self.forecast_dates = pd.date_range(start=self.start_forecast,
161
163
  end=self.end_forecast-self.freq,
162
- freq=self.freq).round(self.freq, ambiguous='infer', nonexistent=self.freq)
164
+ freq=self.freq).round(self.freq, ambiguous='infer', nonexistent='shift_forward')
163
165
  if params is not None:
164
166
  if 'prediction_horizon' in list(self.params['passed_data'].keys()):
165
167
  if self.params['passed_data']['prediction_horizon'] is not None:
@@ -184,7 +186,7 @@ class Forecast(object):
184
186
  freq_scrap = pd.to_timedelta(60, "minutes") # The scrapping time step is 60min
185
187
  forecast_dates_scrap = pd.date_range(start=self.start_forecast,
186
188
  end=self.end_forecast-freq_scrap,
187
- freq=freq_scrap).round(freq_scrap, ambiguous='infer', nonexistent=freq_scrap)
189
+ freq=freq_scrap).round(freq_scrap, ambiguous='infer', nonexistent='shift_forward')
188
190
  # Using the clearoutside webpage
189
191
  response = get("https://clearoutside.com/forecast/"+str(round(self.lat, 2))+"/"+str(round(self.lon, 2))+"?desktop=true")
190
192
  '''import bz2 # Uncomment to save a serialized data for tests
@@ -412,8 +414,10 @@ class Forecast(object):
412
414
  # Setting the main parameters of the PV plant
413
415
  location = Location(latitude=self.lat, longitude=self.lon)
414
416
  temp_params = TEMPERATURE_MODEL_PARAMETERS['sapm']['close_mount_glass_glass']
415
- cec_modules = pvlib.pvsystem.retrieve_sam('CECMod')
416
- cec_inverters = pvlib.pvsystem.retrieve_sam('cecinverter')
417
+ cec_modules = bz2.BZ2File(get_root(__file__, num_parent=2) / 'emhass/data/cec_modules.pbz2', "rb")
418
+ cec_modules = cPickle.load(cec_modules)
419
+ cec_inverters = bz2.BZ2File(get_root(__file__, num_parent=2) / 'emhass/data/cec_inverters.pbz2', "rb")
420
+ cec_inverters = cPickle.load(cec_inverters)
417
421
  if type(self.plant_conf['module_model']) == list:
418
422
  P_PV_forecast = pd.Series(0, index=df_weather.index)
419
423
  for i in range(len(self.plant_conf['module_model'])):
@@ -476,7 +480,7 @@ class Forecast(object):
476
480
  end_forecast_csv = (start_forecast_csv + self.optim_conf['delta_forecast']).replace(microsecond=0)
477
481
  forecast_dates_csv = pd.date_range(start=start_forecast_csv,
478
482
  end=end_forecast_csv+timedelta(days=timedelta_days)-self.freq,
479
- freq=self.freq).round(self.freq, ambiguous='infer', nonexistent=self.freq)
483
+ freq=self.freq).round(self.freq, ambiguous='infer', nonexistent='shift_forward')
480
484
  if self.params is not None:
481
485
  if 'prediction_horizon' in list(self.params['passed_data'].keys()):
482
486
  if self.params['passed_data']['prediction_horizon'] is not None:
emhass/optimization.py CHANGED
@@ -162,10 +162,10 @@ class Optimization:
162
162
 
163
163
  ## Add decision variables
164
164
  P_grid_neg = {(i):plp.LpVariable(cat='Continuous',
165
- lowBound=-self.plant_conf['P_grid_max'], upBound=0,
165
+ lowBound=-self.plant_conf['P_to_grid_max'], upBound=0,
166
166
  name="P_grid_neg{}".format(i)) for i in set_I}
167
167
  P_grid_pos = {(i):plp.LpVariable(cat='Continuous',
168
- lowBound=0, upBound=self.plant_conf['P_grid_max'],
168
+ lowBound=0, upBound=self.plant_conf['P_from_grid_max'],
169
169
  name="P_grid_pos{}".format(i)) for i in set_I}
170
170
  P_deferrable = []
171
171
  P_def_bin1 = []
@@ -267,13 +267,13 @@ class Optimization:
267
267
  # Avoid injecting and consuming from grid at the same time
268
268
  constraints.update({"constraint_pgridpos_{}".format(i) :
269
269
  plp.LpConstraint(
270
- e = P_grid_pos[i] - self.plant_conf['P_grid_max']*D[i],
270
+ e = P_grid_pos[i] - self.plant_conf['P_from_grid_max']*D[i],
271
271
  sense = plp.LpConstraintLE,
272
272
  rhs = 0)
273
273
  for i in set_I})
274
274
  constraints.update({"constraint_pgridneg_{}".format(i) :
275
275
  plp.LpConstraint(
276
- e = -P_grid_neg[i] - self.plant_conf['P_grid_max']*(1-D[i]),
276
+ e = -P_grid_neg[i] - self.plant_conf['P_to_grid_max']*(1-D[i]),
277
277
  sense = plp.LpConstraintLE,
278
278
  rhs = 0)
279
279
  for i in set_I})
emhass/retrieve_hass.py CHANGED
@@ -133,14 +133,14 @@ class RetrieveHass:
133
133
  try: # Sometimes when there are connection problems we need to catch empty retrieved json
134
134
  data = response.json()[0]
135
135
  except IndexError:
136
- if x is 0:
136
+ if x == 0:
137
137
  self.logger.error("The retrieved JSON is empty, A sensor:" + var + " may have 0 days of history or passed sensor may not be correct")
138
138
  else:
139
139
  self.logger.error("The retrieved JSON is empty for day:"+ str(day) +", days_to_retrieve may be larger than the recorded history of sensor:" + var + " (check your recorder settings)")
140
140
  return False
141
141
  df_raw = pd.DataFrame.from_dict(data)
142
142
  if len(df_raw) == 0:
143
- if x is 0:
143
+ if x == 0:
144
144
  self.logger.error("The retrieved Dataframe is empty, A sensor:" + var + " may have 0 days of history or passed sensor may not be correct")
145
145
  else:
146
146
  self.logger.error("Retrieved empty Dataframe for day:"+ str(day) +", days_to_retrieve may be larger than the recorded history of sensor:" + var + " (check your recorder settings)")
@@ -149,7 +149,7 @@ class RetrieveHass:
149
149
  from_date = pd.to_datetime(df_raw['last_changed'], format="ISO8601").min()
150
150
  to_date = pd.to_datetime(df_raw['last_changed'], format="ISO8601").max()
151
151
  ts = pd.to_datetime(pd.date_range(start=from_date, end=to_date, freq=self.freq),
152
- format='%Y-%d-%m %H:%M').round(self.freq, ambiguous='infer', nonexistent=self.freq)
152
+ format='%Y-%d-%m %H:%M').round(self.freq, ambiguous='infer', nonexistent='shift_forward')
153
153
  df_day = pd.DataFrame(index = ts)
154
154
  # Caution with undefined string data: unknown, unavailable, etc.
155
155
  df_tp = df_raw.copy()[['state']].replace(
emhass/static/script.js CHANGED
@@ -1,8 +1,3 @@
1
- //before page load check for stylesheet
2
- document.onreadystatechange = async function() {
3
- checkStyleSheets()
4
- }
5
-
6
1
  //on page reload get saved data
7
2
  window.onload = async function () {
8
3
 
@@ -12,23 +7,6 @@ window.onload = async function () {
12
7
  document.getElementById("basicOrAdvanced").addEventListener("click", () => SwitchBasicOrAdvanced());
13
8
  };
14
9
 
15
- //check style sheet is loaded
16
- async function checkStyleSheets() {
17
- var styleHREF = getHTMLURL() + `static/style.css`
18
- var styles = document.styleSheets;
19
- for (var i = 0; i < styles.length; i++) {
20
- if (styles[i].href.match("style")["input"] == styleHREF) {
21
- return true
22
- }
23
- }
24
- //if could not find file
25
- var style = document.createElement("link");
26
- style.rel = "stylesheet";
27
- style.href = styleHREF;
28
- style.type = "text/css";
29
- document.getElementsByTagName("head")[0].appendChild(style);
30
- }
31
-
32
10
  //add listeners to buttons (based on page)
33
11
  function loadButtons(page) {
34
12
  switch (page) {
@@ -120,19 +98,10 @@ function SwitchBasicOrAdvanced() {
120
98
  }
121
99
  }
122
100
 
123
- //set current url
124
- function getHTMLURL() {
125
- var currentUrl
126
- if (window.location) {
127
- currentUrl = window.location.href; //get current url to append
128
- }
129
- else { currentUrl = "" }
130
- return currentUrl
131
- }
132
101
 
133
102
  //get html data from basic.html or advanced.html
134
103
  async function getHTMLData(htmlFile) {
135
- const response = await fetch(getHTMLURL() + `static/` + htmlFile);
104
+ const response = await fetch(`static/` + htmlFile);
136
105
  blob = await response.blob(); //get data blob
137
106
  htmlTemplateData = await new Response(blob).text(); //obtain html from blob
138
107
  return await htmlTemplateData;
@@ -148,7 +117,7 @@ async function formAction(action, page) {
148
117
 
149
118
  if (data !== 0) { //don't run if there is an error in the input (box/list) Json data
150
119
  showChangeStatus("loading", {}); // show loading div for status
151
- const response = await fetch(getHTMLURL() + `action/${action}`, {
120
+ const response = await fetch(`action/` + action, {
152
121
  //fetch data from webserver.py
153
122
  method: "POST",
154
123
  headers: {
@@ -206,7 +175,7 @@ async function showChangeStatus(status, logJson) {
206
175
  async function getTemplate() {
207
176
  //fetch data from webserver.py
208
177
  let htmlTemplateData = "";
209
- response = await fetch(getHTMLURL() + `template/table-template`, {
178
+ response = await fetch(`template/table-template`, {
210
179
  method: "GET",
211
180
  });
212
181
  blob = await response.blob(); //get data blob
emhass/static/style.css CHANGED
@@ -550,16 +550,16 @@ https://github.com/feathericons/feather */
550
550
  stroke-linecap: round !important;
551
551
  stroke-linejoin: round !important;
552
552
  fill: none !important;
553
- filter: drop-shadow( #282928 .2px .2px) !important;
553
+ filter: drop-shadow(#282928 .2px .2px) !important;
554
554
  -webkit-text-size-adjust: none !important;
555
555
  -ms-text-size-adjust: none !important;
556
556
 
557
557
  }
558
558
 
559
- /* feather icons no background color */
559
+ /* feather icons no background color */
560
560
  #top-links a {
561
561
  background: none !important;
562
- }
562
+ }
563
563
 
564
564
  /* -------------- */
565
565
 
@@ -586,7 +586,8 @@ select {
586
586
  }
587
587
 
588
588
 
589
- .alert, .info {
589
+ .alert,
590
+ .info {
590
591
  max-width: 50%;
591
592
  }
592
593
 
@@ -600,7 +601,7 @@ h2 {
600
601
  margin-bottom: .3em;
601
602
  }
602
603
 
603
- .table_div h4{
604
+ .table_div h4 {
604
605
  margin-top: .5em;
605
606
  }
606
607
 
@@ -683,17 +684,49 @@ button {
683
684
 
684
685
  th {
685
686
  padding: 5px 7.77px;
687
+ text-align: center;
686
688
  }
687
689
 
688
- .mystyle tr:nth-child(even) {
690
+ .mystyle tr:nth-child(even) td,
691
+ th {
689
692
  background: #e1e1e1;
690
693
  }
691
694
 
692
- .mystyle tr:hover {
693
- background: silver;
695
+ .mystyle tr:nth-child(odd) td {
696
+ background: white;
697
+ }
698
+
699
+ .mystyle tr:hover td {
700
+ background-color: silver;
694
701
  cursor: pointer;
695
702
  }
696
703
 
704
+ th:last-child {
705
+ border-top-right-radius: 7px;
706
+ }
707
+
708
+ th:first-child {
709
+ border-top-left-radius: 7px;
710
+ }
711
+
712
+ tr:last-child td:first-child {
713
+ border-bottom-left-radius: 7px;
714
+ }
715
+
716
+ tr:last-child td:last-child {
717
+ border-bottom-right-radius: 7px;
718
+ }
719
+
720
+ tr:hover td:first-child {
721
+ border-top-left-radius: 7px;
722
+ border-bottom-left-radius: 7px;
723
+ }
724
+
725
+ tr:hover td:last-child {
726
+ border-top-right-radius: 7px;
727
+ border-bottom-right-radius: 7px;
728
+ }
729
+
697
730
  #top-links {
698
731
  display: flex;
699
732
  position: absolute;
@@ -795,14 +828,20 @@ th {
795
828
  }
796
829
 
797
830
  /* Basic and Advanced fade transitions */
798
- .TabSelection, #advance, #basic, button, select, .info {
831
+ .TabSelection,
832
+ #advance,
833
+ #basic,
834
+ button,
835
+ select,
836
+ .info {
799
837
  animation-name: fadeInOpacity;
800
838
  animation-iteration-count: 1;
801
839
  animation-timing-function: ease-in-out;
802
840
  animation-duration: .3s;
803
841
  }
804
842
 
805
- .input-list, .input-box {
843
+ .input-list,
844
+ .input-box {
806
845
  animation-name: fadeInOpacity;
807
846
  animation-iteration-count: 1;
808
847
  animation-timing-function: ease-in-out;
@@ -944,7 +983,8 @@ th {
944
983
  display: none !important;
945
984
  }
946
985
 
947
- .info, .alert {
986
+ .info,
987
+ .alert {
948
988
  max-width: 100%;
949
989
  }
950
990
  }
@@ -957,15 +997,17 @@ th {
957
997
  }
958
998
 
959
999
  img,
960
- figure, svg.main-svg{
1000
+ figure,
1001
+ svg.main-svg {
961
1002
  -webkit-filter: invert(.82);
962
1003
  filter: invert(.82);
963
1004
  }
964
-
965
- figure, svg.main-svg{
966
- border-color: #181818;
967
- border-style: solid;
968
- border-width: 1px;
1005
+
1006
+ figure,
1007
+ svg.main-svg {
1008
+ border-color: #181818;
1009
+ border-style: solid;
1010
+ border-width: 1px;
969
1011
  }
970
1012
 
971
1013
  button,
@@ -1008,10 +1050,12 @@ th {
1008
1050
  .modebar-btn svg path {
1009
1051
  fill: #111 !important;
1010
1052
  }
1053
+
1011
1054
  .modebar-btn svg {
1012
- filter:invert(100%) sepia(64%) saturate(2%) hue-rotate(294deg) brightness(85%) contrast(93%) !important
1055
+ filter: invert(100%) sepia(64%) saturate(2%) hue-rotate(294deg) brightness(85%) contrast(93%) !important
1013
1056
  }
1014
- .modebar-btn--logo svg{
1057
+
1058
+ .modebar-btn--logo svg {
1015
1059
  filter: None !important;
1016
1060
  /* filter: invert(100%) saturate(100%) brightness(87%) contrast(100%) !important */
1017
1061
  }
@@ -1024,37 +1068,28 @@ th {
1024
1068
  color: #e1e1e1;
1025
1069
  }
1026
1070
 
1027
- tr td:nth-child(even),
1028
- .mystyle tr td:nth-child(even) {
1029
- background-color: #3d3d3d;
1071
+ .mystyle tr {
1072
+ background: none;
1030
1073
  }
1031
1074
 
1032
- tr:nth-child(odd),
1033
- .mystyle tr:nth-child(odd) {
1034
- background-color: #111111;
1075
+ .mystyle tr:nth-child(even) td,
1076
+ th {
1077
+ background: #282928;
1035
1078
  }
1036
1079
 
1037
- tr:nth-child(even),
1038
- .mystyle tr:nth-child(even) {
1039
- background-color: #181818;
1080
+ .mystyle tr:nth-child(odd) td {
1081
+ background: #111111;
1040
1082
  }
1041
1083
 
1042
- tr:hover td,
1043
- th {
1084
+ .mystyle tr:hover td {
1044
1085
  background-color: #3f3f3f;
1045
1086
  }
1046
1087
 
1047
- tr:hover td,
1048
- th:last-child, th:first-child, td:first-child, td:last-child
1049
- {
1050
- border-radius: 7px
1051
- }
1052
-
1053
- .modebar-group{
1088
+ .modebar-group {
1054
1089
  background-color: #0000 !important;
1055
1090
  }
1056
1091
 
1057
- .modebar-btn{
1092
+ .modebar-btn {
1058
1093
  background: #3f3f3f;
1059
1094
  }
1060
1095
 
@@ -1076,4 +1111,4 @@ th {
1076
1111
  }
1077
1112
 
1078
1113
 
1079
- }
1114
+ }
@@ -4,9 +4,9 @@
4
4
  <head>
5
5
  <title>EMHASS: Energy Management Optimization for Home Assistant</title>
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <link rel="stylesheet" type="text/css" href="{{ basename }}/static/style.css">
8
- <link rel="icon" type="image/x-icon" href="{{ basename }}/static/img/emhass_logo_short.svg">
9
- <script src="{{ basename }}/static/script.js"></script>
7
+ <link rel="stylesheet" type="text/css" href="static/style.css?version=1"> <!-- change version on stylesheet changes -->
8
+ <link rel="icon" type="image/x-icon" href="static/img/emhass_logo_short.svg">
9
+ <script src="static/script.js"></script>
10
10
  </head>
11
11
 
12
12
  <body style="margin: auto; align-items:center; text-align:center;">
@@ -17,22 +17,22 @@
17
17
  <!-- advanced or basic page switch -->
18
18
  <a id="basicOrAdvanced" style="margin-right: 24px; cursor: pointer; z-index: 1">
19
19
  <svg class="feather">
20
- <use class="feather" href="{{ basename }}/static/img/feather-sprite.svg#tool" />
20
+ <use class="feather" href="static/img/feather-sprite.svg#tool" />
21
21
  </svg>
22
22
  </a>
23
23
  <a href="https://emhass.readthedocs.io/en/latest/">
24
24
  <svg class="feather" style="margin-right: 12px;";>
25
- <use class="feather" href="{{ basename }}/static/img/feather-sprite.svg#book" />
25
+ <use class="feather" href="static/img/feather-sprite.svg#book" />
26
26
  </svg>
27
27
  </a>
28
28
  <a href="https://github.com/davidusb-geek/emhass" target="_blank" rel="noopener noreferrer">
29
29
  <svg class="feather" style="margin-right: 0px;" >
30
- <use class="feather" href="{{ basename }}/static/img/feather-sprite.svg#git-branch" />
30
+ <use class="feather" href="static/img/feather-sprite.svg#git-branch" />
31
31
  </svg>
32
32
  </a>
33
33
  </div>
34
34
  <!-- Title -->
35
- <img src="{{ basename }}/static/img/emhass_icon.png" alt="">
35
+ <img src="static/img/emhass_icon.png" alt="">
36
36
  <h2>EMHASS: Energy Management Optimization for Home Assistant</h2>
37
37
  </div>
38
38
 
emhass/utils.py CHANGED
@@ -96,7 +96,7 @@ def get_forecast_dates(freq: int, delta_forecast: int,
96
96
  end_forecast = (start_forecast + pd.Timedelta(days=delta_forecast)).replace(microsecond=0)
97
97
  forecast_dates = pd.date_range(start=start_forecast,
98
98
  end=end_forecast+timedelta(days=timedelta_days)-freq,
99
- freq=freq).round(freq, ambiguous='infer', nonexistent=freq)
99
+ freq=freq).round(freq, ambiguous='infer', nonexistent='shift_forward')
100
100
  return forecast_dates
101
101
 
102
102
  def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dict, optim_conf: dict, plant_conf: dict,
@@ -208,62 +208,23 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
208
208
  params['passed_data']['alpha'] = None
209
209
  params['passed_data']['beta'] = None
210
210
  # Treat passed forecast data lists
211
- if 'pv_power_forecast' in runtimeparams.keys():
212
- if type(runtimeparams['pv_power_forecast']) == list and len(runtimeparams['pv_power_forecast']) >= len(forecast_dates):
213
- params['passed_data']['pv_power_forecast'] = runtimeparams['pv_power_forecast']
214
- optim_conf['weather_forecast_method'] = 'list'
211
+ list_forecast_key = ['pv_power_forecast', 'load_power_forecast', 'load_cost_forecast', 'prod_price_forecast']
212
+ forecast_methods = ['weather_forecast_method', 'load_forecast_method', 'load_cost_forecast_method', 'prod_price_forecast_method']
213
+ for method, forecast_key in enumerate(list_forecast_key):
214
+ if forecast_key in runtimeparams.keys():
215
+ if type(runtimeparams[forecast_key]) == list and len(runtimeparams[forecast_key]) >= len(forecast_dates):
216
+ params['passed_data'][forecast_key] = runtimeparams[forecast_key]
217
+ optim_conf[forecast_methods[method]] = 'list'
218
+ else:
219
+ logger.error(f"ERROR: The passed data is either not a list or the length is not correct, length should be {str(len(forecast_dates))}")
220
+ logger.error(f"Passed type is {str(type(runtimeparams[forecast_key]))} and length is {str(len(runtimeparams[forecast_key]))}")
221
+ list_non_digits = [x for x in runtimeparams[forecast_key] if not (isinstance(x, int) or isinstance(x, float))]
222
+ if len(list_non_digits) > 0:
223
+ logger.warning(f"There are non numeric values on the passed data for {forecast_key}, check for missing values (nans, null, etc)")
224
+ for x in list_non_digits:
225
+ logger.warning(f"This value in {forecast_key} was detected as non digits: {str(x)}")
215
226
  else:
216
- logger.error("ERROR: The passed data is either not a list or the length is not correct, length should be "+str(len(forecast_dates)))
217
- logger.error("Passed type is "+str(type(runtimeparams['pv_power_forecast']))+" and length is "+str(len(runtimeparams['pv_power_forecast'])))
218
- list_non_digits = [x for x in runtimeparams['pv_power_forecast'] if not (isinstance(x, int) or isinstance(x, float))]
219
- if len(list_non_digits) > 0:
220
- logger.warning("There are non numeric values on the passed data for pv_power_forecast, check for missing values (nans, null, etc)")
221
- for x in list_non_digits:
222
- logger.warning("This value in pv_power_forecast was detected as non digits: "+str(x))
223
- else:
224
- params['passed_data']['pv_power_forecast'] = None
225
- if 'load_power_forecast' in runtimeparams.keys():
226
- if type(runtimeparams['load_power_forecast']) == list and len(runtimeparams['load_power_forecast']) >= len(forecast_dates):
227
- params['passed_data']['load_power_forecast'] = runtimeparams['load_power_forecast']
228
- optim_conf['load_forecast_method'] = 'list'
229
- else:
230
- logger.error("ERROR: The passed data is either not a list or the length is not correct, length should be "+str(len(forecast_dates)))
231
- logger.error("Passed type is "+str(type(runtimeparams['load_power_forecast']))+" and length is "+str(len(runtimeparams['load_power_forecast'])))
232
- list_non_digits = [x for x in runtimeparams['load_power_forecast'] if not (isinstance(x, int) or isinstance(x, float))]
233
- if len(list_non_digits) > 0:
234
- logger.warning("There are non numeric values on the passed data for load_power_forecast, check for missing values (nans, null, etc)")
235
- for x in list_non_digits:
236
- logger.warning("This value in load_power_forecast was detected as non digits: "+str(x))
237
- else:
238
- params['passed_data']['load_power_forecast'] = None
239
- if 'load_cost_forecast' in runtimeparams.keys():
240
- if type(runtimeparams['load_cost_forecast']) == list and len(runtimeparams['load_cost_forecast']) >= len(forecast_dates):
241
- params['passed_data']['load_cost_forecast'] = runtimeparams['load_cost_forecast']
242
- optim_conf['load_cost_forecast_method'] = 'list'
243
- else:
244
- logger.error("ERROR: The passed data is either not a list or the length is not correct, length should be "+str(len(forecast_dates)))
245
- logger.error("Passed type is "+str(type(runtimeparams['load_cost_forecast']))+" and length is "+str(len(runtimeparams['load_cost_forecast'])))
246
- list_non_digits = [x for x in runtimeparams['load_cost_forecast'] if not (isinstance(x, int) or isinstance(x, float))]
247
- if len(list_non_digits) > 0:
248
- logger.warning("There are non numeric values on the passed data or load_cost_forecast, check for missing values (nans, null, etc)")
249
- for x in list_non_digits:
250
- logger.warning("This value in load_cost_forecast was detected as non digits: "+str(x))
251
- else:
252
- params['passed_data']['load_cost_forecast'] = None
253
- if 'prod_price_forecast' in runtimeparams.keys():
254
- if type(runtimeparams['prod_price_forecast']) == list and len(runtimeparams['prod_price_forecast']) >= len(forecast_dates):
255
- params['passed_data']['prod_price_forecast'] = runtimeparams['prod_price_forecast']
256
- optim_conf['prod_price_forecast_method'] = 'list'
257
- else:
258
- logger.error("ERROR: The passed data is either not a list or the length is not correct, length should be "+str(len(forecast_dates)))
259
- logger.error("Passed type is "+str(type(runtimeparams['prod_price_forecast']))+" and length is "+str(len(runtimeparams['prod_price_forecast'])))
260
- list_non_digits = [x for x in runtimeparams['prod_price_forecast'] if not (isinstance(x, int) or isinstance(x, float))]
261
- if len(list_non_digits) > 0:
262
- logger.warning("There are non numeric values on the passed data for prod_price_forecast, check for missing values (nans, null, etc)")
263
- for x in list_non_digits:
264
- logger.warning("This value in prod_price_forecast was detected as non digits: "+str(x))
265
- else:
266
- params['passed_data']['prod_price_forecast'] = None
227
+ params['passed_data'][forecast_key] = None
267
228
  # Treat passed data for forecast model fit/predict/tune at runtime
268
229
  if 'days_to_retrieve' not in runtimeparams.keys():
269
230
  days_to_retrieve = 9
@@ -634,8 +595,9 @@ def build_params(params: dict, params_secrets: dict, options: dict, addon: int,
634
595
  params['optim_conf']['def_start_timestep'] = [i['start_timesteps_of_each_deferrable_load'] for i in options.get('list_start_timesteps_of_each_deferrable_load')]
635
596
  if options.get('list_end_timesteps_of_each_deferrable_load',None) != None:
636
597
  params['optim_conf']['def_end_timestep'] = [i['end_timesteps_of_each_deferrable_load'] for i in options.get('list_end_timesteps_of_each_deferrable_load')]
637
- # Updating variables in plant_con
638
- params['plant_conf']['P_grid_max'] = options.get('maximum_power_from_grid',params['plant_conf']['P_grid_max'])
598
+ # Updating variables in plant_conf
599
+ params['plant_conf']['P_from_grid_max'] = options.get('maximum_power_from_grid',params['plant_conf']['P_from_grid_max'])
600
+ params['plant_conf']['P_to_grid_max'] = options.get('maximum_power_to_grid',params['plant_conf']['P_to_grid_max'])
639
601
  if options.get('list_pv_module_model',None) != None:
640
602
  params['plant_conf']['module_model'] = [i['pv_module_model'] for i in options.get('list_pv_module_model')]
641
603
  if options.get('list_pv_inverter_model',None) != None:
@@ -656,8 +618,7 @@ def build_params(params: dict, params_secrets: dict, options: dict, addon: int,
656
618
  params['plant_conf']['SOCmin'] = options.get('battery_minimum_state_of_charge',params['plant_conf']['SOCmin'])
657
619
  params['plant_conf']['SOCmax'] = options.get('battery_maximum_state_of_charge',params['plant_conf']['SOCmax'])
658
620
  params['plant_conf']['SOCtarget'] = options.get('battery_target_state_of_charge',params['plant_conf']['SOCtarget'])
659
-
660
- # Check parameter lists have the same amounts as deferrable loads
621
+ # Check parameter lists have the same amounts as deferrable loads
661
622
  # If not, set defaults it fill in gaps
662
623
  if params['optim_conf']['num_def_loads'] is not len(params['optim_conf']['def_start_timestep']):
663
624
  logger.warning("def_start_timestep / list_start_timesteps_of_each_deferrable_load does not match number in num_def_loads, adding default values to parameter")
@@ -683,10 +644,6 @@ def build_params(params: dict, params_secrets: dict, options: dict, addon: int,
683
644
  logger.warning("P_deferrable_nom / list_nominal_power_of_deferrable_loads does not match number in num_def_loads, adding default values to parameter")
684
645
  for x in range(len(params['optim_conf']['P_deferrable_nom']), params['optim_conf']['num_def_loads']):
685
646
  params['optim_conf']['P_deferrable_nom'].append(0)
686
- if params['optim_conf']['num_def_loads'] is not len(params['optim_conf']['list_hp_periods']):
687
- logger.warning("list_hp_periods / list_peak_hours_periods_(start&end)_hours does not match number in num_def_loads, adding default values to parameter")
688
- for x in range(len(params['optim_conf']['list_hp_periods']), params['optim_conf']['num_def_loads']):
689
- params['optim_conf']['list_hp_periods'].append({'period_hp_'+str(x+1):[{'start':'02:54'},{'end':'20:24'}]})
690
647
  # days_to_retrieve should be no less then 2
691
648
  if params['retrieve_hass_conf']['days_to_retrieve'] < 2:
692
649
  params['retrieve_hass_conf']['days_to_retrieve'] = 2
emhass/web_server.py CHANGED
@@ -69,8 +69,13 @@ def index():
69
69
  else:
70
70
  app.logger.warning("The data container dictionary is empty... Please launch an optimization task")
71
71
  injection_dict={}
72
- basename = request.headers.get("X-Ingress-Path", "")
73
- return make_response(template.render(injection_dict=injection_dict, basename=basename))
72
+
73
+ # replace {{basename}} in html template html with path root
74
+ # basename = request.headers.get("X-Ingress-Path", "")
75
+ # return make_response(template.render(injection_dict=injection_dict, basename=basename))
76
+
77
+ return make_response(template.render(injection_dict=injection_dict))
78
+
74
79
 
75
80
  #get actions
76
81
  @app.route('/template/<action_name>', methods=['GET'])
@@ -86,8 +91,7 @@ def template_action(action_name):
86
91
  else:
87
92
  app.logger.warning("The data container dictionary is empty... Please launch an optimization task")
88
93
  injection_dict={}
89
- basename = request.headers.get("X-Ingress-Path", "")
90
- return make_response(template.render(injection_dict=injection_dict, basename=basename))
94
+ return make_response(template.render(injection_dict=injection_dict))
91
95
 
92
96
  #post actions
93
97
  @app.route('/action/<action_name>', methods=['POST'])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: emhass
3
- Version: 0.8.4
3
+ Version: 0.8.5
4
4
  Summary: An Energy Management System for Home Assistant
5
5
  Home-page: https://github.com/davidusb-geek/emhass
6
6
  Author: David HERNANDEZ
@@ -12,7 +12,7 @@ Classifier: Topic :: Software Development :: Build Tools
12
12
  Classifier: License :: OSI Approved :: MIT License
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Operating System :: OS Independent
15
- Requires-Python: >=3.9, <3.12
15
+ Requires-Python: >=3.10, <3.12
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: wheel
@@ -466,8 +466,6 @@ curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0,
466
466
  curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6,"def_total_hours":[1,3],"def_start_timestep":[0,3],"def_end_timestep":[0,6]}' http://localhost:5000/action/naive-mpc-optim
467
467
  ```
468
468
 
469
-
470
-
471
469
  ## A machine learning forecaster
472
470
 
473
471
  Starting in v0.4.0 a new machine learning forecaster class was introduced.
@@ -0,0 +1,25 @@
1
+ emhass/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ emhass/command_line.py,sha256=pfkGRAyWsYDIVP9WRxb2PZlwhH0KpcFH7uMpDH6gMqc,37462
3
+ emhass/forecast.py,sha256=8sA9r70SfmxKx1BEU7NuJWkN70-0agoh-2GPoDaimWQ,43796
4
+ emhass/machine_learning_forecaster.py,sha256=8Rm0-pltsjIYqLv01zCeO_Ij_n2HKC62dv_kCno7UsU,15640
5
+ emhass/optimization.py,sha256=WcUJSDSBK7wgx0jaX25mhco7ZfqG1g066Ebh6ACyruQ,37197
6
+ emhass/retrieve_hass.py,sha256=COf8LD6B0arFI-P71PXyLT7snB7_Wg5c3bMhRdVMdI4,18406
7
+ emhass/utils.py,sha256=zj1rzpzsRpifgDcmeqRZUcM6WL6GGCcUAdStmGoXlJE,42394
8
+ emhass/web_server.py,sha256=FFdIZio-QGFH3t-p-Le2Q1o6_cqjfBSdoEXdExJ21nY,21541
9
+ emhass/data/cec_inverters.pbz2,sha256=tK8FvAUDW0uYez8EPttdCJwHhpPofclYV6GhhNZL0Pk,168272
10
+ emhass/data/cec_modules.pbz2,sha256=8vEaysgYffXg3KUl8XSF36Mdywzi3LpEtUN_qenjO9s,1655747
11
+ emhass/static/advanced.html,sha256=AsT3lMD0AjvAqzAYvUPmslyOYk2C3LA-VfoSB2PwnYA,1747
12
+ emhass/static/basic.html,sha256=hJ4EgCXVNHL5nMQWkIHWjsTm_bJb0N_ZN4zFUjhxEzU,608
13
+ emhass/static/script.js,sha256=bj3Pksm97sM4lUTpp3IkStx8fMwee39GnDWhrWjvV_A,17250
14
+ emhass/static/style.css,sha256=xSihd06G-AeMKtFGPCJAnTaGVQXKuPM7kvVvDuQDuxU,15557
15
+ emhass/static/img/emhass_icon.png,sha256=Kyx6hXQ1huJLHAq2CaBfjYXR25H9j99PSWHI0lShkaQ,19030
16
+ emhass/static/img/emhass_logo_short.svg,sha256=yzMcqtBRCV8rH84-MwnigZh45_f9Eoqwho9P8nCodJA,66736
17
+ emhass/static/img/feather-sprite.svg,sha256=VHjMJQg88wXa9CaeYrKGhNtyK0xdd47zCqwSIa-hxo8,60319
18
+ emhass/templates/index.html,sha256=_BsvUJ981uSQkx5H9tq_3es__x4WdPiOy7FjNoNYU9w,2744
19
+ emhass/templates/template.html,sha256=TkGgMecQEbFUZA4ymPwMUzNjKHsENvCgroUWbPt7G4Y,158
20
+ emhass-0.8.5.dist-info/LICENSE,sha256=1X3-S1yvOCBDBeox1aK3dq00m7dA8NDtcPrpKPISzbE,1077
21
+ emhass-0.8.5.dist-info/METADATA,sha256=yrzLpT7uN4YHmgR9Unjbp_ypd5qSgR_fG48_RRypRHg,34758
22
+ emhass-0.8.5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
23
+ emhass-0.8.5.dist-info/entry_points.txt,sha256=6Bp1NFOGNv_fSTxYl1ke3K3h3aqAcBxI-bgq5yq-i1M,52
24
+ emhass-0.8.5.dist-info/top_level.txt,sha256=L7fIX4awfmxQbAePtSdVg2e6x_HhghfReHfsKSpKr9I,7
25
+ emhass-0.8.5.dist-info/RECORD,,
@@ -1,23 +0,0 @@
1
- emhass/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- emhass/command_line.py,sha256=Os1W93kSIIiJ_IU48qfkEw691_ywaI0WknRBUDWleYc,37464
3
- emhass/forecast.py,sha256=2_Dm03_XyEVPYnKnJzA5TABYgt3tCe9_zRDGOOfFtEM,43555
4
- emhass/machine_learning_forecaster.py,sha256=8Rm0-pltsjIYqLv01zCeO_Ij_n2HKC62dv_kCno7UsU,15640
5
- emhass/optimization.py,sha256=M9BlbJ4f38APoIsHKLY_pfKszVWA61cPv_QnmtazkRA,37181
6
- emhass/retrieve_hass.py,sha256=buVvu-PJcphN9rlIhW1lI7oYk5r0fHh3h450eqznIAU,18400
7
- emhass/utils.py,sha256=tH6a7ofXpa18Gnll08wCHUdLMzHqqM3aEC-9-nyXKd4,46151
8
- emhass/web_server.py,sha256=U8aqg0udqxChBMxr83b0bE84HZLnuLQyb6SQaJmKtzM,21475
9
- emhass/static/advanced.html,sha256=AsT3lMD0AjvAqzAYvUPmslyOYk2C3LA-VfoSB2PwnYA,1747
10
- emhass/static/basic.html,sha256=hJ4EgCXVNHL5nMQWkIHWjsTm_bJb0N_ZN4zFUjhxEzU,608
11
- emhass/static/script.js,sha256=m6Bxqx1aHWcboXSiAqeEv8XscVC7W9sw8-Z_gPQzp5c,18173
12
- emhass/static/style.css,sha256=HSMn-URvvTvOXs_uVK3Ln7GM2xAvAlJjXdoNVWAdFk0,15222
13
- emhass/static/img/emhass_icon.png,sha256=Kyx6hXQ1huJLHAq2CaBfjYXR25H9j99PSWHI0lShkaQ,19030
14
- emhass/static/img/emhass_logo_short.svg,sha256=yzMcqtBRCV8rH84-MwnigZh45_f9Eoqwho9P8nCodJA,66736
15
- emhass/static/img/feather-sprite.svg,sha256=VHjMJQg88wXa9CaeYrKGhNtyK0xdd47zCqwSIa-hxo8,60319
16
- emhass/templates/index.html,sha256=pZCThgtHXEB6p2JlrMFJrbC3o35PcBmdome3OnNH_SE,2792
17
- emhass/templates/template.html,sha256=TkGgMecQEbFUZA4ymPwMUzNjKHsENvCgroUWbPt7G4Y,158
18
- emhass-0.8.4.dist-info/LICENSE,sha256=1X3-S1yvOCBDBeox1aK3dq00m7dA8NDtcPrpKPISzbE,1077
19
- emhass-0.8.4.dist-info/METADATA,sha256=90908Yq-TiZHviiU1JMCErLMOqTJpuA9xzvx1cxCn7M,34759
20
- emhass-0.8.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
21
- emhass-0.8.4.dist-info/entry_points.txt,sha256=6Bp1NFOGNv_fSTxYl1ke3K3h3aqAcBxI-bgq5yq-i1M,52
22
- emhass-0.8.4.dist-info/top_level.txt,sha256=L7fIX4awfmxQbAePtSdVg2e6x_HhghfReHfsKSpKr9I,7
23
- emhass-0.8.4.dist-info/RECORD,,
File without changes