emhass 0.9.0__py3-none-any.whl → 0.10.0__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 +392 -286
- emhass/forecast.py +21 -21
- emhass/machine_learning_regressor.py +14 -53
- emhass/optimization.py +261 -82
- emhass/retrieve_hass.py +76 -101
- emhass/utils.py +70 -128
- emhass/web_server.py +32 -7
- {emhass-0.9.0.dist-info → emhass-0.10.0.dist-info}/METADATA +125 -19
- {emhass-0.9.0.dist-info → emhass-0.10.0.dist-info}/RECORD +13 -13
- {emhass-0.9.0.dist-info → emhass-0.10.0.dist-info}/LICENSE +0 -0
- {emhass-0.9.0.dist-info → emhass-0.10.0.dist-info}/WHEEL +0 -0
- {emhass-0.9.0.dist-info → emhass-0.10.0.dist-info}/entry_points.txt +0 -0
- {emhass-0.9.0.dist-info → emhass-0.10.0.dist-info}/top_level.txt +0 -0
emhass/retrieve_hass.py
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
import json
|
5
5
|
import copy
|
6
|
+
import os
|
7
|
+
import pathlib
|
6
8
|
import datetime
|
7
9
|
import logging
|
8
10
|
from typing import Optional
|
@@ -61,18 +63,13 @@ class RetrieveHass:
|
|
61
63
|
self.freq = freq
|
62
64
|
self.time_zone = time_zone
|
63
65
|
self.params = params
|
64
|
-
|
66
|
+
self.emhass_conf = emhass_conf
|
65
67
|
self.logger = logger
|
66
68
|
self.get_data_from_file = get_data_from_file
|
67
69
|
|
68
|
-
def get_data(
|
69
|
-
|
70
|
-
|
71
|
-
var_list: list,
|
72
|
-
minimal_response: Optional[bool] = False,
|
73
|
-
significant_changes_only: Optional[bool] = False,
|
74
|
-
test_url: Optional[str] = "empty",
|
75
|
-
) -> None:
|
70
|
+
def get_data(self, days_list: pd.date_range, var_list: list,
|
71
|
+
minimal_response: Optional[bool] = False, significant_changes_only: Optional[bool] = False,
|
72
|
+
test_url: Optional[str] = "empty") -> None:
|
76
73
|
r"""
|
77
74
|
Retrieve the actual data from hass.
|
78
75
|
|
@@ -100,9 +97,7 @@ class RetrieveHass:
|
|
100
97
|
x = 0 # iterate based on days
|
101
98
|
# Looping on each day from days list
|
102
99
|
for day in days_list:
|
103
|
-
|
104
100
|
for i, var in enumerate(var_list):
|
105
|
-
|
106
101
|
if test_url == "empty":
|
107
102
|
if (
|
108
103
|
self.hass_url == "http://supervisor/core/api"
|
@@ -289,15 +284,8 @@ class RetrieveHass:
|
|
289
284
|
return True
|
290
285
|
|
291
286
|
@staticmethod
|
292
|
-
def get_attr_data_dict(
|
293
|
-
|
294
|
-
idx: int,
|
295
|
-
entity_id: str,
|
296
|
-
unit_of_measurement: str,
|
297
|
-
friendly_name: str,
|
298
|
-
list_name: str,
|
299
|
-
state: float,
|
300
|
-
) -> dict:
|
287
|
+
def get_attr_data_dict(data_df: pd.DataFrame, idx: int, entity_id: str, unit_of_measurement: str,
|
288
|
+
friendly_name: str, list_name: str, state: float) -> dict:
|
301
289
|
list_df = copy.deepcopy(data_df).loc[data_df.index[idx] :].reset_index()
|
302
290
|
list_df.columns = ["timestamps", entity_id]
|
303
291
|
ts_list = [str(i) for i in list_df["timestamps"].tolist()]
|
@@ -318,17 +306,11 @@ class RetrieveHass:
|
|
318
306
|
}
|
319
307
|
return data
|
320
308
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
unit_of_measurement: str,
|
327
|
-
friendly_name: str,
|
328
|
-
type_var: str,
|
329
|
-
from_mlforecaster: Optional[bool] = False,
|
330
|
-
publish_prefix: Optional[str] = "",
|
331
|
-
) -> None:
|
309
|
+
|
310
|
+
def post_data(self, data_df: pd.DataFrame, idx: int, entity_id: str, unit_of_measurement: str,
|
311
|
+
friendly_name: str, type_var: str, from_mlforecaster: Optional[bool] = False,
|
312
|
+
publish_prefix: Optional[str] = "", save_entities: Optional[bool] = False,
|
313
|
+
logger_levels: Optional[str] = "info", dont_post: Optional[bool] = False) -> None:
|
332
314
|
r"""
|
333
315
|
Post passed data to hass.
|
334
316
|
|
@@ -348,6 +330,12 @@ class RetrieveHass:
|
|
348
330
|
:type type_var: str
|
349
331
|
:param publish_prefix: A common prefix for all published data entity_id.
|
350
332
|
:type publish_prefix: str, optional
|
333
|
+
:param save_entities: if entity data should be saved in data_path/entities
|
334
|
+
:type save_entities: bool, optional
|
335
|
+
:param logger_levels: set logger level, info or debug, to output
|
336
|
+
:type logger_levels: str, optional
|
337
|
+
:param dont_post: dont post to HA
|
338
|
+
:type dont_post: bool, optional
|
351
339
|
|
352
340
|
"""
|
353
341
|
# Add a possible prefix to the entity ID
|
@@ -362,10 +350,12 @@ class RetrieveHass:
|
|
362
350
|
headers = {
|
363
351
|
"Authorization": "Bearer " + self.long_lived_token,
|
364
352
|
"content-type": "application/json",
|
365
|
-
}
|
353
|
+
}
|
366
354
|
# Preparing the data dict to be published
|
367
355
|
if type_var == "cost_fun":
|
368
|
-
|
356
|
+
if isinstance(data_df.iloc[0],pd.Series): #if Series extract
|
357
|
+
data_df = data_df.iloc[:, 0]
|
358
|
+
state = np.round(data_df.sum(), 2)
|
369
359
|
elif type_var == "unit_load_cost" or type_var == "unit_prod_price":
|
370
360
|
state = np.round(data_df.loc[data_df.index[idx]], 4)
|
371
361
|
elif type_var == "optim_status":
|
@@ -375,75 +365,26 @@ class RetrieveHass:
|
|
375
365
|
else:
|
376
366
|
state = np.round(data_df.loc[data_df.index[idx]], 2)
|
377
367
|
if type_var == "power":
|
378
|
-
data = RetrieveHass.get_attr_data_dict(
|
379
|
-
|
380
|
-
idx,
|
381
|
-
entity_id,
|
382
|
-
unit_of_measurement,
|
383
|
-
friendly_name,
|
384
|
-
"forecasts",
|
385
|
-
state,
|
386
|
-
)
|
368
|
+
data = RetrieveHass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
369
|
+
friendly_name, "forecasts", state)
|
387
370
|
elif type_var == "deferrable":
|
388
|
-
data = RetrieveHass.get_attr_data_dict(
|
389
|
-
|
390
|
-
idx,
|
391
|
-
entity_id,
|
392
|
-
unit_of_measurement,
|
393
|
-
friendly_name,
|
394
|
-
"deferrables_schedule",
|
395
|
-
state,
|
396
|
-
)
|
371
|
+
data = RetrieveHass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
372
|
+
friendly_name, "deferrables_schedule", state)
|
397
373
|
elif type_var == "batt":
|
398
|
-
data = RetrieveHass.get_attr_data_dict(
|
399
|
-
|
400
|
-
idx,
|
401
|
-
entity_id,
|
402
|
-
unit_of_measurement,
|
403
|
-
friendly_name,
|
404
|
-
"battery_scheduled_power",
|
405
|
-
state,
|
406
|
-
)
|
374
|
+
data = RetrieveHass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
375
|
+
friendly_name, "battery_scheduled_power", state)
|
407
376
|
elif type_var == "SOC":
|
408
|
-
data = RetrieveHass.get_attr_data_dict(
|
409
|
-
|
410
|
-
idx,
|
411
|
-
entity_id,
|
412
|
-
unit_of_measurement,
|
413
|
-
friendly_name,
|
414
|
-
"battery_scheduled_soc",
|
415
|
-
state,
|
416
|
-
)
|
377
|
+
data = RetrieveHass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
378
|
+
friendly_name, "battery_scheduled_soc", state)
|
417
379
|
elif type_var == "unit_load_cost":
|
418
|
-
data = RetrieveHass.get_attr_data_dict(
|
419
|
-
|
420
|
-
idx,
|
421
|
-
entity_id,
|
422
|
-
unit_of_measurement,
|
423
|
-
friendly_name,
|
424
|
-
"unit_load_cost_forecasts",
|
425
|
-
state,
|
426
|
-
)
|
380
|
+
data = RetrieveHass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
381
|
+
friendly_name, "unit_load_cost_forecasts", state)
|
427
382
|
elif type_var == "unit_prod_price":
|
428
|
-
data = RetrieveHass.get_attr_data_dict(
|
429
|
-
|
430
|
-
idx,
|
431
|
-
entity_id,
|
432
|
-
unit_of_measurement,
|
433
|
-
friendly_name,
|
434
|
-
"unit_prod_price_forecasts",
|
435
|
-
state,
|
436
|
-
)
|
383
|
+
data = RetrieveHass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
384
|
+
friendly_name, "unit_prod_price_forecasts", state)
|
437
385
|
elif type_var == "mlforecaster":
|
438
|
-
data = RetrieveHass.get_attr_data_dict(
|
439
|
-
|
440
|
-
idx,
|
441
|
-
entity_id,
|
442
|
-
unit_of_measurement,
|
443
|
-
friendly_name,
|
444
|
-
"scheduled_forecast",
|
445
|
-
state,
|
446
|
-
)
|
386
|
+
data = RetrieveHass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
387
|
+
friendly_name, "scheduled_forecast", state)
|
447
388
|
elif type_var == "optim_status":
|
448
389
|
data = {
|
449
390
|
"state": state,
|
@@ -469,20 +410,54 @@ class RetrieveHass:
|
|
469
410
|
},
|
470
411
|
}
|
471
412
|
# Actually post the data
|
472
|
-
if self.get_data_from_file:
|
473
|
-
|
413
|
+
if self.get_data_from_file or dont_post:
|
474
414
|
class response:
|
475
415
|
pass
|
476
|
-
|
477
416
|
response.status_code = 200
|
478
417
|
response.ok = True
|
479
418
|
else:
|
480
419
|
response = post(url, headers=headers, data=json.dumps(data))
|
420
|
+
|
481
421
|
# Treating the response status and posting them on the logger
|
482
422
|
if response.ok:
|
483
|
-
|
423
|
+
|
424
|
+
if logger_levels == "DEBUG":
|
425
|
+
self.logger.debug("Successfully posted to " + entity_id + " = " + str(state))
|
426
|
+
else:
|
427
|
+
self.logger.info("Successfully posted to " + entity_id + " = " + str(state))
|
428
|
+
|
429
|
+
# If save entities is set, save entity data to /data_path/entities
|
430
|
+
if (save_entities):
|
431
|
+
entities_path = self.emhass_conf['data_path'] / "entities"
|
432
|
+
|
433
|
+
# Clarify folder exists
|
434
|
+
pathlib.Path(entities_path).mkdir(parents=True, exist_ok=True)
|
435
|
+
|
436
|
+
# Save entity data to json file
|
437
|
+
result = data_df.to_json(index="timestamp", orient='index', date_unit='s', date_format='iso')
|
438
|
+
parsed = json.loads(result)
|
439
|
+
with open(entities_path / (entity_id + ".json"), "w") as file:
|
440
|
+
json.dump(parsed, file, indent=4)
|
441
|
+
|
442
|
+
# Save the required metadata to json file
|
443
|
+
if os.path.isfile(entities_path / "metadata.json"):
|
444
|
+
with open(entities_path / "metadata.json", "r") as file:
|
445
|
+
metadata = json.load(file)
|
446
|
+
else:
|
447
|
+
metadata = {}
|
448
|
+
with open(entities_path / "metadata.json", "w") as file:
|
449
|
+
# Save entity metadata, key = entity_id
|
450
|
+
metadata[entity_id] = {'name': data_df.name, 'unit_of_measurement': unit_of_measurement,'friendly_name': friendly_name,'type_var': type_var, 'freq': int(self.freq.seconds / 60)}
|
451
|
+
|
452
|
+
# Find lowest frequency to set for continual loop freq
|
453
|
+
if metadata.get("lowest_freq",None) == None or metadata["lowest_freq"] > int(self.freq.seconds / 60):
|
454
|
+
metadata["lowest_freq"] = int(self.freq.seconds / 60)
|
455
|
+
json.dump(metadata,file, indent=4)
|
456
|
+
|
457
|
+
self.logger.debug("Saved " + entity_id + " to json file")
|
458
|
+
|
484
459
|
else:
|
485
|
-
self.logger.
|
460
|
+
self.logger.warning(
|
486
461
|
"The status code for received curl command response is: "
|
487
462
|
+ str(response.status_code)
|
488
463
|
)
|
emhass/utils.py
CHANGED
@@ -12,7 +12,6 @@ import pandas as pd
|
|
12
12
|
import yaml
|
13
13
|
import pytz
|
14
14
|
|
15
|
-
|
16
15
|
import plotly.express as px
|
17
16
|
|
18
17
|
pd.options.plotting.backend = "plotly"
|
@@ -41,6 +40,7 @@ def get_root(file: str, num_parent: Optional[int] = 3) -> str:
|
|
41
40
|
raise ValueError("num_parent value not valid, must be between 1 and 3")
|
42
41
|
return root
|
43
42
|
|
43
|
+
|
44
44
|
def get_logger(fun_name: str, emhass_conf: dict, save_to_file: Optional[bool] = True,
|
45
45
|
logging_level: Optional[str] = "DEBUG") -> Tuple[logging.Logger, logging.StreamHandler]:
|
46
46
|
"""
|
@@ -88,9 +88,8 @@ def get_logger(fun_name: str, emhass_conf: dict, save_to_file: Optional[bool] =
|
|
88
88
|
return logger, ch
|
89
89
|
|
90
90
|
|
91
|
-
def get_forecast_dates(
|
92
|
-
|
93
|
-
) -> pd.core.indexes.datetimes.DatetimeIndex:
|
91
|
+
def get_forecast_dates(freq: int, delta_forecast: int, timedelta_days: Optional[int] = 0
|
92
|
+
) -> pd.core.indexes.datetimes.DatetimeIndex:
|
94
93
|
"""
|
95
94
|
Get the date_range list of the needed future dates using the delta_forecast parameter.
|
96
95
|
|
@@ -113,15 +112,9 @@ def get_forecast_dates(
|
|
113
112
|
return forecast_dates
|
114
113
|
|
115
114
|
|
116
|
-
def treat_runtimeparams(
|
117
|
-
|
118
|
-
|
119
|
-
retrieve_hass_conf: dict,
|
120
|
-
optim_conf: dict,
|
121
|
-
plant_conf: dict,
|
122
|
-
set_type: str,
|
123
|
-
logger: logging.Logger,
|
124
|
-
) -> Tuple[str, dict]:
|
115
|
+
def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dict, optim_conf: dict,
|
116
|
+
plant_conf: dict, set_type: str, logger: logging.Logger
|
117
|
+
) -> Tuple[str, dict]:
|
125
118
|
"""
|
126
119
|
Treat the passed optimization runtime parameters.
|
127
120
|
|
@@ -419,6 +412,8 @@ def treat_runtimeparams(
|
|
419
412
|
optim_conf["def_start_timestep"] = runtimeparams["def_start_timestep"]
|
420
413
|
if "def_end_timestep" in runtimeparams.keys():
|
421
414
|
optim_conf["def_end_timestep"] = runtimeparams["def_end_timestep"]
|
415
|
+
if "def_current_state" in runtimeparams.keys():
|
416
|
+
optim_conf["def_current_state"] = [bool(s) for s in runtimeparams["def_current_state"]]
|
422
417
|
if "treat_def_as_semi_cont" in runtimeparams.keys():
|
423
418
|
optim_conf["treat_def_as_semi_cont"] = [
|
424
419
|
eval(str(k).capitalize())
|
@@ -449,6 +444,8 @@ def treat_runtimeparams(
|
|
449
444
|
optim_conf["weight_battery_charge"] = runtimeparams["weight_battery_charge"]
|
450
445
|
if 'freq' in runtimeparams.keys():
|
451
446
|
retrieve_hass_conf['freq'] = pd.to_timedelta(runtimeparams['freq'], "minutes")
|
447
|
+
if 'continual_publish' in runtimeparams.keys():
|
448
|
+
retrieve_hass_conf['continual_publish'] = bool(runtimeparams['continual_publish'])
|
452
449
|
# Treat plant configuration parameters passed at runtime
|
453
450
|
if "SOCtarget" in runtimeparams.keys():
|
454
451
|
plant_conf["SOCtarget"] = runtimeparams["SOCtarget"]
|
@@ -493,16 +490,23 @@ def treat_runtimeparams(
|
|
493
490
|
params["passed_data"]["custom_deferrable_forecast_id"] = runtimeparams[
|
494
491
|
"custom_deferrable_forecast_id"
|
495
492
|
]
|
496
|
-
# A condition to put a prefix on all published data
|
493
|
+
# A condition to put a prefix on all published data, or check for saved data under prefix name
|
497
494
|
if "publish_prefix" not in runtimeparams.keys():
|
498
495
|
publish_prefix = ""
|
499
496
|
else:
|
500
497
|
publish_prefix = runtimeparams["publish_prefix"]
|
501
498
|
params["passed_data"]["publish_prefix"] = publish_prefix
|
499
|
+
# A condition to manually save entity data under data_path/entities after optimization
|
500
|
+
if "entity_save" not in runtimeparams.keys():
|
501
|
+
entity_save = ""
|
502
|
+
else:
|
503
|
+
entity_save = runtimeparams["entity_save"]
|
504
|
+
params["passed_data"]["entity_save"] = entity_save
|
502
505
|
# Serialize the final params
|
503
506
|
params = json.dumps(params)
|
504
507
|
return params, retrieve_hass_conf, optim_conf, plant_conf
|
505
508
|
|
509
|
+
|
506
510
|
def get_yaml_parse(emhass_conf: dict, use_secrets: Optional[bool] = True,
|
507
511
|
params: Optional[str] = None) -> Tuple[dict, dict, dict]:
|
508
512
|
"""
|
@@ -526,7 +530,7 @@ def get_yaml_parse(emhass_conf: dict, use_secrets: Optional[bool] = True,
|
|
526
530
|
input_conf = json.loads(params)
|
527
531
|
if use_secrets:
|
528
532
|
if params is None:
|
529
|
-
with open(emhass_conf["
|
533
|
+
with open(emhass_conf["config_path"].parent / 'secrets_emhass.yaml', 'r') as file: # Assume secrets and config file paths are the same
|
530
534
|
input_secrets = yaml.load(file, Loader=yaml.FullLoader)
|
531
535
|
else:
|
532
536
|
input_secrets = input_conf.pop("params_secrets", None)
|
@@ -655,9 +659,7 @@ def get_injection_dict(df: pd.DataFrame, plot_size: Optional[int] = 1366) -> dic
|
|
655
659
|
return injection_dict
|
656
660
|
|
657
661
|
|
658
|
-
def get_injection_dict_forecast_model_fit(
|
659
|
-
df_fit_pred: pd.DataFrame, mlf: MLForecaster
|
660
|
-
) -> dict:
|
662
|
+
def get_injection_dict_forecast_model_fit(df_fit_pred: pd.DataFrame, mlf: MLForecaster) -> dict:
|
661
663
|
"""
|
662
664
|
Build a dictionary with graphs and tables for the webui for special MLF fit case.
|
663
665
|
|
@@ -686,9 +688,7 @@ def get_injection_dict_forecast_model_fit(
|
|
686
688
|
return injection_dict
|
687
689
|
|
688
690
|
|
689
|
-
def get_injection_dict_forecast_model_tune(
|
690
|
-
df_pred_optim: pd.DataFrame, mlf: MLForecaster
|
691
|
-
) -> dict:
|
691
|
+
def get_injection_dict_forecast_model_tune(df_pred_optim: pd.DataFrame, mlf: MLForecaster) -> dict:
|
692
692
|
"""
|
693
693
|
Build a dictionary with graphs and tables for the webui for special MLF tune case.
|
694
694
|
|
@@ -719,13 +719,8 @@ def get_injection_dict_forecast_model_tune(
|
|
719
719
|
return injection_dict
|
720
720
|
|
721
721
|
|
722
|
-
def build_params(
|
723
|
-
|
724
|
-
params_secrets: dict,
|
725
|
-
options: dict,
|
726
|
-
addon: int,
|
727
|
-
logger: logging.Logger,
|
728
|
-
) -> dict:
|
722
|
+
def build_params(params: dict, params_secrets: dict, options: dict, addon: int,
|
723
|
+
logger: logging.Logger) -> dict:
|
729
724
|
"""
|
730
725
|
Build the main params dictionary from the loaded options.json when using the add-on.
|
731
726
|
|
@@ -744,60 +739,30 @@ def build_params(
|
|
744
739
|
"""
|
745
740
|
if addon == 1:
|
746
741
|
# Updating variables in retrieve_hass_conf
|
747
|
-
params["retrieve_hass_conf"]["freq"] = options.get(
|
748
|
-
|
749
|
-
)
|
750
|
-
params["retrieve_hass_conf"]["
|
751
|
-
|
752
|
-
|
753
|
-
)
|
754
|
-
params["retrieve_hass_conf"]["var_PV"] = options.get(
|
755
|
-
"sensor_power_photovoltaics", params["retrieve_hass_conf"]["var_PV"]
|
756
|
-
)
|
757
|
-
params["retrieve_hass_conf"]["var_load"] = options.get(
|
758
|
-
"sensor_power_load_no_var_loads", params["retrieve_hass_conf"]["var_load"]
|
759
|
-
)
|
760
|
-
params["retrieve_hass_conf"]["load_negative"] = options.get(
|
761
|
-
"load_negative", params["retrieve_hass_conf"]["load_negative"]
|
762
|
-
)
|
763
|
-
params["retrieve_hass_conf"]["set_zero_min"] = options.get(
|
764
|
-
"set_zero_min", params["retrieve_hass_conf"]["set_zero_min"]
|
765
|
-
)
|
742
|
+
params["retrieve_hass_conf"]["freq"] = options.get("optimization_time_step", params["retrieve_hass_conf"]["freq"])
|
743
|
+
params["retrieve_hass_conf"]["days_to_retrieve"] = options.get("historic_days_to_retrieve", params["retrieve_hass_conf"]["days_to_retrieve"])
|
744
|
+
params["retrieve_hass_conf"]["var_PV"] = options.get("sensor_power_photovoltaics", params["retrieve_hass_conf"]["var_PV"])
|
745
|
+
params["retrieve_hass_conf"]["var_load"] = options.get("sensor_power_load_no_var_loads", params["retrieve_hass_conf"]["var_load"])
|
746
|
+
params["retrieve_hass_conf"]["load_negative"] = options.get("load_negative", params["retrieve_hass_conf"]["load_negative"])
|
747
|
+
params["retrieve_hass_conf"]["set_zero_min"] = options.get("set_zero_min", params["retrieve_hass_conf"]["set_zero_min"])
|
766
748
|
params["retrieve_hass_conf"]["var_replace_zero"] = [
|
767
|
-
options.get(
|
768
|
-
"sensor_power_photovoltaics",
|
769
|
-
params["retrieve_hass_conf"]["var_replace_zero"],
|
770
|
-
)
|
749
|
+
options.get("sensor_power_photovoltaics", params["retrieve_hass_conf"]["var_replace_zero"])
|
771
750
|
]
|
772
751
|
params["retrieve_hass_conf"]["var_interp"] = [
|
773
|
-
options.get(
|
774
|
-
|
775
|
-
),
|
776
|
-
options.get(
|
777
|
-
"sensor_power_load_no_var_loads",
|
778
|
-
params["retrieve_hass_conf"]["var_load"],
|
779
|
-
),
|
752
|
+
options.get("sensor_power_photovoltaics", params["retrieve_hass_conf"]["var_PV"]),
|
753
|
+
options.get("sensor_power_load_no_var_loads", params["retrieve_hass_conf"]["var_load"])
|
780
754
|
]
|
781
|
-
params["retrieve_hass_conf"]["method_ts_round"] = options.get(
|
782
|
-
|
783
|
-
)
|
755
|
+
params["retrieve_hass_conf"]["method_ts_round"] = options.get("method_ts_round", params["retrieve_hass_conf"]["method_ts_round"])
|
756
|
+
params["retrieve_hass_conf"]["continual_publish"] = options.get("continual_publish", params["retrieve_hass_conf"]["continual_publish"])
|
784
757
|
# Update params Secrets if specified
|
785
758
|
params["params_secrets"] = params_secrets
|
786
|
-
params["params_secrets"]["time_zone"] = options.get(
|
787
|
-
"time_zone", params_secrets["time_zone"]
|
788
|
-
)
|
759
|
+
params["params_secrets"]["time_zone"] = options.get("time_zone", params_secrets["time_zone"])
|
789
760
|
params["params_secrets"]["lat"] = options.get("Latitude", params_secrets["lat"])
|
790
|
-
params["params_secrets"]["lon"] = options.get(
|
791
|
-
"Longitude", params_secrets["lon"]
|
792
|
-
)
|
761
|
+
params["params_secrets"]["lon"] = options.get("Longitude", params_secrets["lon"])
|
793
762
|
params["params_secrets"]["alt"] = options.get("Altitude", params_secrets["alt"])
|
794
763
|
# Updating variables in optim_conf
|
795
|
-
params["optim_conf"]["set_use_battery"] = options.get(
|
796
|
-
|
797
|
-
)
|
798
|
-
params["optim_conf"]["num_def_loads"] = options.get(
|
799
|
-
"number_of_deferrable_loads", params["optim_conf"]["num_def_loads"]
|
800
|
-
)
|
764
|
+
params["optim_conf"]["set_use_battery"] = options.get("set_use_battery", params["optim_conf"]["set_use_battery"])
|
765
|
+
params["optim_conf"]["num_def_loads"] = options.get("number_of_deferrable_loads", params["optim_conf"]["num_def_loads"])
|
801
766
|
if options.get("list_nominal_power_of_deferrable_loads", None) != None:
|
802
767
|
params["optim_conf"]["P_deferrable_nom"] = [
|
803
768
|
i["nominal_power_of_deferrable_loads"]
|
@@ -813,43 +778,22 @@ def build_params(
|
|
813
778
|
i["treat_deferrable_load_as_semi_cont"]
|
814
779
|
for i in options.get("list_treat_deferrable_load_as_semi_cont")
|
815
780
|
]
|
816
|
-
params["optim_conf"]["weather_forecast_method"] = options.get(
|
817
|
-
"weather_forecast_method", params["optim_conf"]["weather_forecast_method"]
|
818
|
-
)
|
781
|
+
params["optim_conf"]["weather_forecast_method"] = options.get("weather_forecast_method", params["optim_conf"]["weather_forecast_method"])
|
819
782
|
# Update optional param secrets
|
820
783
|
if params["optim_conf"]["weather_forecast_method"] == "solcast":
|
821
|
-
params["params_secrets"]["solcast_api_key"] = options.get(
|
822
|
-
|
823
|
-
params_secrets.get("solcast_api_key", "123456"),
|
824
|
-
)
|
825
|
-
params["params_secrets"]["solcast_rooftop_id"] = options.get(
|
826
|
-
"optional_solcast_rooftop_id",
|
827
|
-
params_secrets.get("solcast_rooftop_id", "123456"),
|
828
|
-
)
|
784
|
+
params["params_secrets"]["solcast_api_key"] = options.get("optional_solcast_api_key", params_secrets.get("solcast_api_key", "123456"))
|
785
|
+
params["params_secrets"]["solcast_rooftop_id"] = options.get("optional_solcast_rooftop_id", params_secrets.get("solcast_rooftop_id", "123456"))
|
829
786
|
elif params["optim_conf"]["weather_forecast_method"] == "solar.forecast":
|
830
|
-
params["params_secrets"]["solar_forecast_kwp"] = options.get(
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
params["optim_conf"]["load_forecast_method"] = options.get(
|
835
|
-
"load_forecast_method", params["optim_conf"]["load_forecast_method"]
|
836
|
-
)
|
837
|
-
params["optim_conf"]["delta_forecast"] = options.get(
|
838
|
-
"delta_forecast_daily", params["optim_conf"]["delta_forecast"]
|
839
|
-
)
|
840
|
-
params["optim_conf"]["load_cost_forecast_method"] = options.get(
|
841
|
-
"load_cost_forecast_method",
|
842
|
-
params["optim_conf"]["load_cost_forecast_method"],
|
843
|
-
)
|
787
|
+
params["params_secrets"]["solar_forecast_kwp"] = options.get("optional_solar_forecast_kwp", params_secrets.get("solar_forecast_kwp", 5))
|
788
|
+
params["optim_conf"]["load_forecast_method"] = options.get("load_forecast_method", params["optim_conf"]["load_forecast_method"])
|
789
|
+
params["optim_conf"]["delta_forecast"] = options.get("delta_forecast_daily", params["optim_conf"]["delta_forecast"])
|
790
|
+
params["optim_conf"]["load_cost_forecast_method"] = options.get("load_cost_forecast_method", params["optim_conf"]["load_cost_forecast_method"])
|
844
791
|
if options.get("list_set_deferrable_load_single_constant", None) != None:
|
845
792
|
params["optim_conf"]["set_def_constant"] = [
|
846
793
|
i["set_deferrable_load_single_constant"]
|
847
794
|
for i in options.get("list_set_deferrable_load_single_constant")
|
848
795
|
]
|
849
|
-
if (
|
850
|
-
options.get("list_peak_hours_periods_start_hours", None) != None
|
851
|
-
and options.get("list_peak_hours_periods_end_hours", None) != None
|
852
|
-
):
|
796
|
+
if (options.get("list_peak_hours_periods_start_hours", None) != None and options.get("list_peak_hours_periods_end_hours", None) != None):
|
853
797
|
start_hours_list = [
|
854
798
|
i["peak_hours_periods_start_hours"]
|
855
799
|
for i in options["list_peak_hours_periods_start_hours"]
|
@@ -861,27 +805,27 @@ def build_params(
|
|
861
805
|
num_peak_hours = len(start_hours_list)
|
862
806
|
list_hp_periods_list = [{'period_hp_'+str(i+1):[{'start':start_hours_list[i]},{'end':end_hours_list[i]}]} for i in range(num_peak_hours)]
|
863
807
|
params['optim_conf']['list_hp_periods'] = list_hp_periods_list
|
864
|
-
params['optim_conf']['load_cost_hp'] = options.get('load_peak_hours_cost',params['optim_conf']['load_cost_hp'])
|
808
|
+
params['optim_conf']['load_cost_hp'] = options.get('load_peak_hours_cost', params['optim_conf']['load_cost_hp'])
|
865
809
|
params['optim_conf']['load_cost_hc'] = options.get('load_offpeak_hours_cost', params['optim_conf']['load_cost_hc'])
|
866
810
|
params['optim_conf']['prod_price_forecast_method'] = options.get('production_price_forecast_method', params['optim_conf']['prod_price_forecast_method'])
|
867
|
-
params['optim_conf']['prod_sell_price'] = options.get('photovoltaic_production_sell_price',params['optim_conf']['prod_sell_price'])
|
868
|
-
params['optim_conf']['set_total_pv_sell'] = options.get('set_total_pv_sell',params['optim_conf']['set_total_pv_sell'])
|
869
|
-
params['optim_conf']['lp_solver'] = options.get('lp_solver',params['optim_conf']['lp_solver'])
|
870
|
-
params['optim_conf']['lp_solver_path'] = options.get('lp_solver_path',params['optim_conf']['lp_solver_path'])
|
871
|
-
params['optim_conf']['set_nocharge_from_grid'] = options.get('set_nocharge_from_grid',params['optim_conf']['set_nocharge_from_grid'])
|
872
|
-
params['optim_conf']['set_nodischarge_to_grid'] = options.get('set_nodischarge_to_grid',params['optim_conf']['set_nodischarge_to_grid'])
|
873
|
-
params['optim_conf']['set_battery_dynamic'] = options.get('set_battery_dynamic',params['optim_conf']['set_battery_dynamic'])
|
874
|
-
params['optim_conf']['battery_dynamic_max'] = options.get('battery_dynamic_max',params['optim_conf']['battery_dynamic_max'])
|
875
|
-
params['optim_conf']['battery_dynamic_min'] = options.get('battery_dynamic_min',params['optim_conf']['battery_dynamic_min'])
|
876
|
-
params['optim_conf']['weight_battery_discharge'] = options.get('weight_battery_discharge',params['optim_conf']['weight_battery_discharge'])
|
877
|
-
params['optim_conf']['weight_battery_charge'] = options.get('weight_battery_charge',params['optim_conf']['weight_battery_charge'])
|
811
|
+
params['optim_conf']['prod_sell_price'] = options.get('photovoltaic_production_sell_price', params['optim_conf']['prod_sell_price'])
|
812
|
+
params['optim_conf']['set_total_pv_sell'] = options.get('set_total_pv_sell', params['optim_conf']['set_total_pv_sell'])
|
813
|
+
params['optim_conf']['lp_solver'] = options.get('lp_solver', params['optim_conf']['lp_solver'])
|
814
|
+
params['optim_conf']['lp_solver_path'] = options.get('lp_solver_path', params['optim_conf']['lp_solver_path'])
|
815
|
+
params['optim_conf']['set_nocharge_from_grid'] = options.get('set_nocharge_from_grid', params['optim_conf']['set_nocharge_from_grid'])
|
816
|
+
params['optim_conf']['set_nodischarge_to_grid'] = options.get('set_nodischarge_to_grid', params['optim_conf']['set_nodischarge_to_grid'])
|
817
|
+
params['optim_conf']['set_battery_dynamic'] = options.get('set_battery_dynamic', params['optim_conf']['set_battery_dynamic'])
|
818
|
+
params['optim_conf']['battery_dynamic_max'] = options.get('battery_dynamic_max', params['optim_conf']['battery_dynamic_max'])
|
819
|
+
params['optim_conf']['battery_dynamic_min'] = options.get('battery_dynamic_min', params['optim_conf']['battery_dynamic_min'])
|
820
|
+
params['optim_conf']['weight_battery_discharge'] = options.get('weight_battery_discharge', params['optim_conf']['weight_battery_discharge'])
|
821
|
+
params['optim_conf']['weight_battery_charge'] = options.get('weight_battery_charge', params['optim_conf']['weight_battery_charge'])
|
878
822
|
if options.get('list_start_timesteps_of_each_deferrable_load',None) != None:
|
879
823
|
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')]
|
880
824
|
if options.get('list_end_timesteps_of_each_deferrable_load',None) != None:
|
881
825
|
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')]
|
882
826
|
# Updating variables in plant_conf
|
883
|
-
params['plant_conf']['P_from_grid_max'] = options.get('maximum_power_from_grid',params['plant_conf']['P_from_grid_max'])
|
884
|
-
params['plant_conf']['P_to_grid_max'] = options.get('maximum_power_to_grid',params['plant_conf']['P_to_grid_max'])
|
827
|
+
params['plant_conf']['P_from_grid_max'] = options.get('maximum_power_from_grid', params['plant_conf']['P_from_grid_max'])
|
828
|
+
params['plant_conf']['P_to_grid_max'] = options.get('maximum_power_to_grid', params['plant_conf']['P_to_grid_max'])
|
885
829
|
if options.get('list_pv_module_model',None) != None:
|
886
830
|
params['plant_conf']['module_model'] = [i['pv_module_model'] for i in options.get('list_pv_module_model')]
|
887
831
|
if options.get('list_pv_inverter_model',None) != None:
|
@@ -894,14 +838,15 @@ def build_params(
|
|
894
838
|
params['plant_conf']['modules_per_string'] = [i['modules_per_string'] for i in options.get('list_modules_per_string')]
|
895
839
|
if options.get('list_strings_per_inverter',None) != None:
|
896
840
|
params['plant_conf']['strings_per_inverter'] = [i['strings_per_inverter'] for i in options.get('list_strings_per_inverter')]
|
897
|
-
params[
|
898
|
-
params['plant_conf']['
|
899
|
-
params['plant_conf']['
|
900
|
-
params['plant_conf']['
|
901
|
-
params['plant_conf']['
|
902
|
-
params['plant_conf']['
|
903
|
-
params['plant_conf']['
|
904
|
-
params['plant_conf']['
|
841
|
+
params["plant_conf"]["inverter_is_hybrid"] = options.get("inverter_is_hybrid", params["plant_conf"]["inverter_is_hybrid"])
|
842
|
+
params['plant_conf']['Pd_max'] = options.get('battery_discharge_power_max', params['plant_conf']['Pd_max'])
|
843
|
+
params['plant_conf']['Pc_max'] = options.get('battery_charge_power_max', params['plant_conf']['Pc_max'])
|
844
|
+
params['plant_conf']['eta_disch'] = options.get('battery_discharge_efficiency', params['plant_conf']['eta_disch'])
|
845
|
+
params['plant_conf']['eta_ch'] = options.get('battery_charge_efficiency', params['plant_conf']['eta_ch'])
|
846
|
+
params['plant_conf']['Enom'] = options.get('battery_nominal_energy_capacity', params['plant_conf']['Enom'])
|
847
|
+
params['plant_conf']['SOCmin'] = options.get('battery_minimum_state_of_charge', params['plant_conf']['SOCmin'])
|
848
|
+
params['plant_conf']['SOCmax'] = options.get('battery_maximum_state_of_charge', params['plant_conf']['SOCmax'])
|
849
|
+
params['plant_conf']['SOCtarget'] = options.get('battery_target_state_of_charge', params['plant_conf']['SOCtarget'])
|
905
850
|
# Check parameter lists have the same amounts as deferrable loads
|
906
851
|
# If not, set defaults it fill in gaps
|
907
852
|
if params['optim_conf']['num_def_loads'] is not len(params['optim_conf']['def_start_timestep']):
|
@@ -931,9 +876,7 @@ def build_params(
|
|
931
876
|
# days_to_retrieve should be no less then 2
|
932
877
|
if params["retrieve_hass_conf"]["days_to_retrieve"] < 2:
|
933
878
|
params["retrieve_hass_conf"]["days_to_retrieve"] = 2
|
934
|
-
logger.warning(
|
935
|
-
"days_to_retrieve should not be lower then 2, setting days_to_retrieve to 2. Make sure your sensors also have at least 2 days of history"
|
936
|
-
)
|
879
|
+
logger.warning("days_to_retrieve should not be lower then 2, setting days_to_retrieve to 2. Make sure your sensors also have at least 2 days of history")
|
937
880
|
else:
|
938
881
|
params["params_secrets"] = params_secrets
|
939
882
|
# The params dict
|
@@ -967,7 +910,6 @@ def get_days_list(days_to_retrieve: int) -> pd.date_range:
|
|
967
910
|
today = datetime.now(timezone.utc).replace(minute=0, second=0, microsecond=0)
|
968
911
|
d = (today - timedelta(days=days_to_retrieve)).isoformat()
|
969
912
|
days_list = pd.date_range(start=d, end=today.isoformat(), freq="D")
|
970
|
-
|
971
913
|
return days_list
|
972
914
|
|
973
915
|
|