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/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
- # self.emhass_conf = emhass_conf
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
- self,
70
- days_list: pd.date_range,
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
- data_df: pd.DataFrame,
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
- def post_data(
322
- self,
323
- data_df: pd.DataFrame,
324
- idx: int,
325
- entity_id: str,
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
- state = np.round(data_df.sum()[0], 2)
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
- data_df,
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
- data_df,
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
- data_df,
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
- data_df,
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
- data_df,
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
- data_df,
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
- data_df,
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
- self.logger.info("Successfully posted to " + entity_id + " = " + str(state))
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.info(
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
- freq: int, delta_forecast: int, timedelta_days: Optional[int] = 0
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
- runtimeparams: str,
118
- params: str,
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["root_path"] / 'secrets_emhass.yaml', 'r') as file: #assume secrets file is in root path
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
- params: dict,
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
- "optimization_time_step", params["retrieve_hass_conf"]["freq"]
749
- )
750
- params["retrieve_hass_conf"]["days_to_retrieve"] = options.get(
751
- "historic_days_to_retrieve",
752
- params["retrieve_hass_conf"]["days_to_retrieve"],
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
- "sensor_power_photovoltaics", params["retrieve_hass_conf"]["var_PV"]
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
- "method_ts_round", params["retrieve_hass_conf"]["method_ts_round"]
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
- "set_use_battery", params["optim_conf"]["set_use_battery"]
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
- "optional_solcast_api_key",
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
- "optional_solar_forecast_kwp",
832
- params_secrets.get("solar_forecast_kwp", 5),
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['plant_conf']['Pd_max'] = options.get('battery_discharge_power_max',params['plant_conf']['Pd_max'])
898
- params['plant_conf']['Pc_max'] = options.get('battery_charge_power_max',params['plant_conf']['Pc_max'])
899
- params['plant_conf']['eta_disch'] = options.get('battery_discharge_efficiency',params['plant_conf']['eta_disch'])
900
- params['plant_conf']['eta_ch'] = options.get('battery_charge_efficiency',params['plant_conf']['eta_ch'])
901
- params['plant_conf']['Enom'] = options.get('battery_nominal_energy_capacity',params['plant_conf']['Enom'])
902
- params['plant_conf']['SOCmin'] = options.get('battery_minimum_state_of_charge',params['plant_conf']['SOCmin'])
903
- params['plant_conf']['SOCmax'] = options.get('battery_maximum_state_of_charge',params['plant_conf']['SOCmax'])
904
- params['plant_conf']['SOCtarget'] = options.get('battery_target_state_of_charge',params['plant_conf']['SOCtarget'])
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