emhass 0.11.0__tar.gz → 0.11.2__tar.gz

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.
Files changed (65) hide show
  1. {emhass-0.11.0 → emhass-0.11.2}/CHANGELOG.md +13 -0
  2. {emhass-0.11.0 → emhass-0.11.2}/PKG-INFO +3 -6
  3. {emhass-0.11.0 → emhass-0.11.2}/README.md +2 -5
  4. {emhass-0.11.0 → emhass-0.11.2}/setup.py +1 -1
  5. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/data/associations.csv +2 -0
  6. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/data/config_defaults.json +1 -2
  7. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/retrieve_hass.py +20 -11
  8. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/configuration_script.js +6 -5
  9. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/utils.py +124 -65
  10. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/web_server.py +0 -1
  11. {emhass-0.11.0 → emhass-0.11.2}/src/emhass.egg-info/PKG-INFO +3 -6
  12. {emhass-0.11.0 → emhass-0.11.2}/tests/test_retrieve_hass.py +12 -6
  13. {emhass-0.11.0 → emhass-0.11.2}/tests/test_utils.py +1 -1
  14. {emhass-0.11.0 → emhass-0.11.2}/CODE_OF_CONDUCT.md +0 -0
  15. {emhass-0.11.0 → emhass-0.11.2}/CONTRIBUTING.md +0 -0
  16. {emhass-0.11.0 → emhass-0.11.2}/LICENSE +0 -0
  17. {emhass-0.11.0 → emhass-0.11.2}/MANIFEST.in +0 -0
  18. {emhass-0.11.0 → emhass-0.11.2}/data/data_load_cost_forecast.csv +0 -0
  19. {emhass-0.11.0 → emhass-0.11.2}/data/data_load_forecast.csv +0 -0
  20. {emhass-0.11.0 → emhass-0.11.2}/data/data_prod_price_forecast.csv +0 -0
  21. {emhass-0.11.0 → emhass-0.11.2}/data/data_train_load_clustering.pkl +0 -0
  22. {emhass-0.11.0 → emhass-0.11.2}/data/data_train_load_forecast.pkl +0 -0
  23. {emhass-0.11.0 → emhass-0.11.2}/data/data_weather_forecast.csv +0 -0
  24. {emhass-0.11.0 → emhass-0.11.2}/data/heating_prediction.csv +0 -0
  25. {emhass-0.11.0 → emhass-0.11.2}/data/opt_res_latest.csv +0 -0
  26. {emhass-0.11.0 → emhass-0.11.2}/data/opt_res_perfect_optim_cost.csv +0 -0
  27. {emhass-0.11.0 → emhass-0.11.2}/data/opt_res_perfect_optim_profit.csv +0 -0
  28. {emhass-0.11.0 → emhass-0.11.2}/data/opt_res_perfect_optim_self-consumption.csv +0 -0
  29. {emhass-0.11.0 → emhass-0.11.2}/data/test_df_final.pkl +0 -0
  30. {emhass-0.11.0 → emhass-0.11.2}/data/test_response_get_data_get_method.pbz2 +0 -0
  31. {emhass-0.11.0 → emhass-0.11.2}/data/test_response_scrapper_get_method.pbz2 +0 -0
  32. {emhass-0.11.0 → emhass-0.11.2}/data/test_response_solarforecast_get_method.pbz2 +0 -0
  33. {emhass-0.11.0 → emhass-0.11.2}/data/test_response_solcast_get_method.pbz2 +0 -0
  34. {emhass-0.11.0 → emhass-0.11.2}/pyproject.toml +0 -0
  35. {emhass-0.11.0 → emhass-0.11.2}/setup.cfg +0 -0
  36. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/__init__.py +0 -0
  37. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/command_line.py +0 -0
  38. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/data/cec_inverters.pbz2 +0 -0
  39. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/data/cec_modules.pbz2 +0 -0
  40. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/forecast.py +0 -0
  41. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/machine_learning_forecaster.py +0 -0
  42. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/machine_learning_regressor.py +0 -0
  43. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/optimization.py +0 -0
  44. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/advanced.html +0 -0
  45. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/basic.html +0 -0
  46. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/configuration_list.html +0 -0
  47. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/data/param_definitions.json +0 -0
  48. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/img/emhass_icon.png +0 -0
  49. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/img/emhass_logo_short.svg +0 -0
  50. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/img/feather-sprite.svg +0 -0
  51. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/script.js +0 -0
  52. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/static/style.css +0 -0
  53. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/templates/configuration.html +0 -0
  54. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/templates/index.html +0 -0
  55. {emhass-0.11.0 → emhass-0.11.2}/src/emhass/templates/template.html +0 -0
  56. {emhass-0.11.0 → emhass-0.11.2}/src/emhass.egg-info/SOURCES.txt +0 -0
  57. {emhass-0.11.0 → emhass-0.11.2}/src/emhass.egg-info/dependency_links.txt +0 -0
  58. {emhass-0.11.0 → emhass-0.11.2}/src/emhass.egg-info/entry_points.txt +0 -0
  59. {emhass-0.11.0 → emhass-0.11.2}/src/emhass.egg-info/requires.txt +0 -0
  60. {emhass-0.11.0 → emhass-0.11.2}/src/emhass.egg-info/top_level.txt +0 -0
  61. {emhass-0.11.0 → emhass-0.11.2}/tests/test_command_line_utils.py +0 -0
  62. {emhass-0.11.0 → emhass-0.11.2}/tests/test_forecast.py +0 -0
  63. {emhass-0.11.0 → emhass-0.11.2}/tests/test_machine_learning_forecaster.py +0 -0
  64. {emhass-0.11.0 → emhass-0.11.2}/tests/test_machine_learning_regressor.py +0 -0
  65. {emhass-0.11.0 → emhass-0.11.2}/tests/test_optimization.py +0 -0
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.11.2 - 2024-10-31
4
+ ### Improvement
5
+ - Added support to retrieve HA configuration. This will be used in the future to automatically retrieve some parameters as the currency
6
+ ### Fix
7
+ - utils fix runtime parameter merge bugs
8
+ - configuration_script.js fix placeholder value bug
9
+
10
+ ## 0.11.1 - 2024-10-29
11
+ ### Fix
12
+ - Fix parameter saving and duplicate battery bugs
13
+ - utils.py add more specific logging information for config
14
+ - Fix issue where thermal runtime parameters were not being propagated into optim_conf
15
+
3
16
  ## 0.11.0 - 2024-10-25
4
17
 
5
18
  This version marks huge improvement works by @GeoDerp aiming to simplfy the intial and normal setup of EMHASS. The workflow for setting the EMHASS configuration regardless of the installation method has now been centralized on the single `config.json` file. The webserver has now a configuration tab that can be used to to modify and save the `config.json` file.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: emhass
3
- Version: 0.11.0
3
+ Version: 0.11.2
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
@@ -47,9 +47,6 @@ Requires-Dist: plotly>=5.6.0
47
47
  </a>
48
48
  <a style="text-decoration:none" href="https://github.com/davidusb-geek/emhass/actions">
49
49
  <img alt="EMHASS GitHub Workflow Status" src="https://github.com/davidusb-geek/emhass/actions/workflows/publish_docker.yaml/badge.svg?event=release">
50
- </a>
51
- <a style="text-decoration:none" href="https://github.com/davidusb-geek/emhass-add-on/actions">
52
- <img alt="EMHASS-Add-on GitHub Workflow Status" src="https://github.com/davidusb-geek/emhass-add-on/actions/workflows/publish_docker.yaml/badge.svg?event=release">
53
50
  </a>
54
51
  <a hstyle="text-decoration:none" ref="https://codecov.io/github/davidusb-geek/emhass" >
55
52
  <img src="https://codecov.io/github/davidusb-geek/emhass/branch/master/graph/badge.svg?token=BW7KSCHN90"/>
@@ -164,7 +161,7 @@ _Note: Both EMHASS via Docker and EMHASS-Add-on contain the same Docker image. T
164
161
  You can also install EMHASS using Docker as a container. This can be in the same machine as Home Assistant (if your running Home Assistant as a Docker container) or in a different distant machine. To install first pull the latest image:
165
162
  ```bash
166
163
  # pull Docker image
167
- docker docker pull ghcr.io/davidusb-geek/emhass:latest
164
+ docker pull ghcr.io/davidusb-geek/emhass:latest
168
165
  # run Docker image, mounting config.json and secrets_emhass.yaml from host
169
166
  docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v ./config.json:/share/config.json -v ./secrets_emhass.yaml:/app/secrets_emhass.yaml ghcr.io/davidusb-geek/emhass:latest
170
167
  ```
@@ -173,7 +170,7 @@ docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v ./
173
170
  You can also build your image locally. For this clone this repository, and build the image from the Dockerfile:
174
171
  ```bash
175
172
  # git clone EMHASS repo
176
- git clone docker pull ghcr.io/geoderp/emhass:v0.21.3
173
+ git clone https://github.com/davidusb-geek/emhass.git
177
174
  # move to EMHASS directory
178
175
  cd emhass
179
176
  # build Docker image
@@ -12,9 +12,6 @@
12
12
  </a>
13
13
  <a style="text-decoration:none" href="https://github.com/davidusb-geek/emhass/actions">
14
14
  <img alt="EMHASS GitHub Workflow Status" src="https://github.com/davidusb-geek/emhass/actions/workflows/publish_docker.yaml/badge.svg?event=release">
15
- </a>
16
- <a style="text-decoration:none" href="https://github.com/davidusb-geek/emhass-add-on/actions">
17
- <img alt="EMHASS-Add-on GitHub Workflow Status" src="https://github.com/davidusb-geek/emhass-add-on/actions/workflows/publish_docker.yaml/badge.svg?event=release">
18
15
  </a>
19
16
  <a hstyle="text-decoration:none" ref="https://codecov.io/github/davidusb-geek/emhass" >
20
17
  <img src="https://codecov.io/github/davidusb-geek/emhass/branch/master/graph/badge.svg?token=BW7KSCHN90"/>
@@ -129,7 +126,7 @@ _Note: Both EMHASS via Docker and EMHASS-Add-on contain the same Docker image. T
129
126
  You can also install EMHASS using Docker as a container. This can be in the same machine as Home Assistant (if your running Home Assistant as a Docker container) or in a different distant machine. To install first pull the latest image:
130
127
  ```bash
131
128
  # pull Docker image
132
- docker docker pull ghcr.io/davidusb-geek/emhass:latest
129
+ docker pull ghcr.io/davidusb-geek/emhass:latest
133
130
  # run Docker image, mounting config.json and secrets_emhass.yaml from host
134
131
  docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v ./config.json:/share/config.json -v ./secrets_emhass.yaml:/app/secrets_emhass.yaml ghcr.io/davidusb-geek/emhass:latest
135
132
  ```
@@ -138,7 +135,7 @@ docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v ./
138
135
  You can also build your image locally. For this clone this repository, and build the image from the Dockerfile:
139
136
  ```bash
140
137
  # git clone EMHASS repo
141
- git clone docker pull ghcr.io/geoderp/emhass:v0.21.3
138
+ git clone https://github.com/davidusb-geek/emhass.git
142
139
  # move to EMHASS directory
143
140
  cd emhass
144
141
  # build Docker image
@@ -19,7 +19,7 @@ long_description = (here / 'README.md').read_text(encoding='utf-8')
19
19
 
20
20
  setup(
21
21
  name='emhass', # Required
22
- version='0.11.0', # Required
22
+ version='0.11.2', # Required
23
23
  description='An Energy Management System for Home Assistant', # Optional
24
24
  long_description=long_description, # Optional
25
25
  long_description_content_type='text/markdown', # Optional (see note above)
@@ -1,4 +1,5 @@
1
1
  config_categorie,legacy_parameter_name,parameter,list_name
2
+ retrieve_hass_conf,logging_level,logging_level
2
3
  retrieve_hass_conf,freq,optimization_time_step
3
4
  retrieve_hass_conf,days_to_retrieve,historic_days_to_retrieve
4
5
  retrieve_hass_conf,var_PV,sensor_power_photovoltaics
@@ -13,6 +14,7 @@ params_secrets,time_zone,time_zone
13
14
  params_secrets,lat,Latitude
14
15
  params_secrets,lon,Longitude
15
16
  params_secrets,alt,Altitude
17
+ optim_conf,costfun,costfun
16
18
  optim_conf,set_use_battery,set_use_battery
17
19
  optim_conf,num_def_loads,number_of_deferrable_loads
18
20
  optim_conf,P_deferrable_nom,nominal_power_of_deferrable_loads,list_nominal_power_of_deferrable_loads
@@ -19,8 +19,7 @@
19
19
  "sensor_power_photovoltaics": "sensor.power_photovoltaics",
20
20
  "sensor_power_load_no_var_loads": "sensor.power_load_no_var_loads",
21
21
  "sensor_replace_zero": [
22
- "sensor.power_photovoltaics",
23
- "sensor.power_load_no_var_loads"
22
+ "sensor.power_photovoltaics"
24
23
  ],
25
24
  "sensor_linear_interp": [
26
25
  "sensor.power_photovoltaics",
@@ -72,6 +72,19 @@ class RetrieveHass:
72
72
  self.logger = logger
73
73
  self.get_data_from_file = get_data_from_file
74
74
 
75
+ def get_ha_config(self):
76
+ """
77
+ Extract some configuration data from HA.
78
+
79
+ """
80
+ headers = {
81
+ "Authorization": "Bearer " + self.long_lived_token,
82
+ "content-type": "application/json"
83
+ }
84
+ url = self.hass_url+"api/config"
85
+ response_config = get(url, headers=headers)
86
+ self.ha_config = response_config.json()
87
+
75
88
  def get_data(self, days_list: pd.date_range, var_list: list,
76
89
  minimal_response: Optional[bool] = False, significant_changes_only: Optional[bool] = False,
77
90
  test_url: Optional[str] = "empty") -> None:
@@ -98,15 +111,17 @@ class RetrieveHass:
98
111
  are experimental
99
112
  """
100
113
  self.logger.info("Retrieve hass get data method initiated...")
114
+ headers = {
115
+ "Authorization": "Bearer " + self.long_lived_token,
116
+ "content-type": "application/json"
117
+ }
118
+ # Looping on each day from days list
101
119
  self.df_final = pd.DataFrame()
102
120
  x = 0 # iterate based on days
103
- # Looping on each day from days list
104
121
  for day in days_list:
105
122
  for i, var in enumerate(var_list):
106
123
  if test_url == "empty":
107
- if (
108
- self.hass_url == "http://supervisor/core/api"
109
- ): # If we are using the supervisor API
124
+ if (self.hass_url == "http://supervisor/core/api"): # If we are using the supervisor API
110
125
  url = (
111
126
  self.hass_url
112
127
  + "/history/period/"
@@ -124,16 +139,10 @@ class RetrieveHass:
124
139
  )
125
140
  if minimal_response: # A support for minimal response
126
141
  url = url + "?minimal_response"
127
- if (
128
- significant_changes_only
129
- ): # And for signicant changes only (check the HASS restful API for more info)
142
+ if (significant_changes_only): # And for signicant changes only (check the HASS restful API for more info)
130
143
  url = url + "?significant_changes_only"
131
144
  else:
132
145
  url = test_url
133
- headers = {
134
- "Authorization": "Bearer " + self.long_lived_token,
135
- "content-type": "application/json",
136
- }
137
146
  try:
138
147
  response = get(url, headers=headers)
139
148
  except Exception:
@@ -249,7 +249,7 @@ function buildParamContainers(
249
249
  `;
250
250
  }
251
251
 
252
- //After looping though, build and appending the parameters in the corresponding section:
252
+ //after looping though, build and appending the parameters in the corresponding section:
253
253
  //create add button (array plus) event listeners
254
254
  let plus = SectionContainer.querySelectorAll(".input-plus");
255
255
  plus.forEach(function (answer) {
@@ -407,7 +407,7 @@ function buildParamElement(
407
407
  else {
408
408
  return `
409
409
  ${type_specific_html}
410
- <input class="param_input" type="${type}" value=${value} placeholder=${parameter_definition_object["default_value"]}>
410
+ <input class="param_input" type="${type}" placeholder=${parameter_definition_object["default_value"]} value=${value} >
411
411
  ${type_specific_html_end}
412
412
  `;
413
413
  }
@@ -418,9 +418,9 @@ function buildParamElement(
418
418
  if (typeof Object.values(value)[0] === "object") {
419
419
  for (param of Object.values(value)) {
420
420
  for (items of Object.values(param)) {
421
- inputs += `<input class="param_input" type="${type}" value=${
421
+ inputs += `<input class="param_input" type="${type}" placeholder=${Object.values(items)[0]} value=${
422
422
  Object.values(items)[0]
423
- } placeholder=${Object.values(items)[0]}>`;
423
+ }>`;
424
424
  }
425
425
  inputs += `</br>`;
426
426
  }
@@ -432,7 +432,7 @@ function buildParamElement(
432
432
  for (param of value) {
433
433
  inputs += `
434
434
  ${type_specific_html}
435
- <input class="param_input" type="${type}" value=${param} placeholder=${parameter_definition_object["default_value"]}>
435
+ <input class="param_input" type="${type}" placeholder=${parameter_definition_object["default_value"]} value=${param}>
436
436
  ${type_specific_html_end}
437
437
  `;
438
438
  }
@@ -549,6 +549,7 @@ function headerElement(element, param_definitions, config) {
549
549
  //if set_use_battery, add or remove battery section (inc. params)
550
550
  case "set_use_battery":
551
551
  if (element.checked) {
552
+ param_container.innerHTML = "";
552
553
  buildParamContainers("Battery", param_definitions["Battery"], config, [
553
554
  "set_use_battery",
554
555
  ]);
@@ -288,7 +288,7 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
288
288
  else:
289
289
  prediction_horizon = runtimeparams["prediction_horizon"]
290
290
  params["passed_data"]["prediction_horizon"] = prediction_horizon
291
- if "soc_init" not in runtimeparams.keys():
291
+ if 'soc_init' not in runtimeparams.keys():
292
292
  soc_init = plant_conf['battery_target_state_of_charge']
293
293
  else:
294
294
  soc_init = runtimeparams["soc_init"]
@@ -298,25 +298,33 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
298
298
  else:
299
299
  soc_final = runtimeparams["soc_final"]
300
300
  params["passed_data"]["soc_final"] = soc_final
301
- if 'operating_hours_of_each_deferrable_load' not in runtimeparams.keys():
302
- def_total_hours = optim_conf['operating_hours_of_each_deferrable_load']
301
+ if 'operating_hours_of_each_deferrable_load' not in runtimeparams.keys() and 'def_total_hours' not in runtimeparams.keys():
302
+ def_total_hours = optim_conf.get('operating_hours_of_each_deferrable_load')
303
303
  else:
304
- def_total_hours = runtimeparams['operating_hours_of_each_deferrable_load']
304
+ def_total_hours = runtimeparams.get(
305
+ 'operating_hours_of_each_deferrable_load', runtimeparams.get('def_total_hours'))
305
306
  params["passed_data"]['operating_hours_of_each_deferrable_load'] = def_total_hours
306
- if 'start_timesteps_of_each_deferrable_load' in runtimeparams.keys():
307
- def_start_timestep = runtimeparams['start_timesteps_of_each_deferrable_load']
308
- else:
307
+ if 'start_timesteps_of_each_deferrable_load' not in runtimeparams.keys() and 'def_start_timestep' in runtimeparams.keys():
308
+ def_start_timestep = optim_conf.get('start_timesteps_of_each_deferrable_load')
309
+ else:
309
310
  def_start_timestep = runtimeparams.get(
310
- 'def_start_timestep', optim_conf['start_timesteps_of_each_deferrable_load'])
311
+ 'start_timesteps_of_each_deferrable_load', runtimeparams.get('def_start_timestep'))
311
312
  params["passed_data"]['start_timesteps_of_each_deferrable_load'] = def_start_timestep
312
- if 'end_timesteps_of_each_deferrable_load' in runtimeparams.keys():
313
- def_end_timestep = runtimeparams['end_timesteps_of_each_deferrable_load']
313
+ if 'end_timesteps_of_each_deferrable_load' not in runtimeparams.keys() and 'def_end_timestep' not in runtimeparams.keys():
314
+ def_end_timestep = optim_conf.get('end_timesteps_of_each_deferrable_load')
314
315
  else:
315
316
  def_end_timestep = runtimeparams.get(
316
- 'def_end_timestep', optim_conf['end_timesteps_of_each_deferrable_load'])
317
+ 'end_timesteps_of_each_deferrable_load', runtimeparams.get('def_end_timestep'))
317
318
  params["passed_data"]['end_timesteps_of_each_deferrable_load'] = def_end_timestep
318
- forecast_dates = copy.deepcopy(forecast_dates)[
319
- 0:prediction_horizon]
319
+ forecast_dates = copy.deepcopy(forecast_dates)[0:prediction_horizon]
320
+ # Load the default config
321
+ if "def_load_config" in optim_conf:
322
+ for k in range(len(optim_conf["def_load_config"])):
323
+ if "thermal_config" in optim_conf["def_load_config"][k]:
324
+ if ("heater_desired_temperatures" in runtimeparams and len(runtimeparams["heater_desired_temperatures"]) > k):
325
+ optim_conf["def_load_config"][k]["thermal_config"]["desired_temperatures"] = runtimeparams["heater_desired_temperatures"][k]
326
+ if ("heater_start_temperatures" in runtimeparams and len(runtimeparams["heater_start_temperatures"]) > k):
327
+ optim_conf["def_load_config"][k]["thermal_config"]["start_temperature"] = runtimeparams["heater_start_temperatures"][k]
320
328
  else:
321
329
  params["passed_data"]["prediction_horizon"] = None
322
330
  params["passed_data"]["soc_init"] = None
@@ -357,11 +365,16 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
357
365
  params['passed_data'][forecast_key] = None
358
366
 
359
367
  # Treat passed data for forecast model fit/predict/tune at runtime
360
- if 'historic_days_to_retrieve' in runtimeparams.keys():
361
- days_to_retrieve = runtimeparams['historic_days_to_retrieve']
368
+ if 'historic_days_to_retrieve' not in runtimeparams.keys() and 'days_to_retrieve' not in runtimeparams.keys():
369
+ historic_days_to_retrieve = retrieve_hass_conf.get('historic_days_to_retrieve')
362
370
  else:
363
- days_to_retrieve = runtimeparams.get('days_to_retrieve', 9)
364
- params["passed_data"]['historic_days_to_retrieve'] = days_to_retrieve
371
+ historic_days_to_retrieve = runtimeparams.get(
372
+ 'historic_days_to_retrieve', runtimeparams.get('days_to_retrieve'))
373
+ if historic_days_to_retrieve < 9:
374
+ logger.warning("warning `days_to_retrieve` is set to a value less than 9, this could cause an error with the fit")
375
+ logger.warning("setting`passed_data:days_to_retrieve` to 9 for fit/predict/tune")
376
+ historic_days_to_retrieve = 9
377
+ params["passed_data"]['historic_days_to_retrieve'] = historic_days_to_retrieve
365
378
  if "model_type" not in runtimeparams.keys():
366
379
  model_type = "load_forecast"
367
380
  else:
@@ -472,50 +485,35 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
472
485
  params["passed_data"]["publish_prefix"] = publish_prefix
473
486
 
474
487
  # Treat optimization (optim_conf) configuration parameters passed at runtime
475
- if 'number_of_deferrable_loads' in runtimeparams.keys():
476
- optim_conf['number_of_deferrable_loads'] = runtimeparams['number_of_deferrable_loads']
477
- if 'num_def_loads' in runtimeparams.keys():
478
- optim_conf['number_of_deferrable_loads'] = runtimeparams['num_def_loads']
479
- if 'nominal_power_of_deferrable_loads' in runtimeparams.keys():
480
- optim_conf['nominal_power_of_deferrable_loads'] = runtimeparams['nominal_power_of_deferrable_loads']
481
- if 'P_deferrable_nom' in runtimeparams.keys():
482
- optim_conf['nominal_power_of_deferrable_loads'] = runtimeparams['P_deferrable_nom']
483
- if 'operating_hours_of_each_deferrable_load' in runtimeparams.keys():
484
- optim_conf['operating_hours_of_each_deferrable_load'] = runtimeparams['operating_hours_of_each_deferrable_load']
485
- if 'def_total_hours' in runtimeparams.keys():
486
- optim_conf['operating_hours_of_each_deferrable_load'] = runtimeparams['def_total_hours']
487
- if 'start_timesteps_of_each_deferrable_load' in runtimeparams.keys():
488
- optim_conf['start_timesteps_of_each_deferrable_load'] = runtimeparams['start_timesteps_of_each_deferrable_load']
489
- if 'end_timesteps_of_each_deferrable_load' in runtimeparams.keys():
490
- optim_conf['end_timesteps_of_each_deferrable_load'] = runtimeparams['end_timesteps_of_each_deferrable_load']
488
+ if 'number_of_deferrable_loads' in runtimeparams.keys() or 'num_def_loads' in runtimeparams.keys():
489
+ optim_conf['number_of_deferrable_loads'] = runtimeparams.get(
490
+ 'number_of_deferrable_loads', runtimeparams.get('num_def_loads'))
491
+ if 'nominal_power_of_deferrable_loads' in runtimeparams.keys() or 'P_deferrable_nom' in runtimeparams.keys():
492
+ optim_conf['nominal_power_of_deferrable_loads'] = runtimeparams.get(
493
+ 'nominal_power_of_deferrable_loads', runtimeparams.get('P_deferrable_nom'))
494
+ if 'operating_hours_of_each_deferrable_load' in runtimeparams.keys() or 'def_total_hours' in runtimeparams.keys():
495
+ optim_conf['operating_hours_of_each_deferrable_load'] = runtimeparams.get(
496
+ 'operating_hours_of_each_deferrable_load', runtimeparams.get('def_total_hours'))
497
+ if 'start_timesteps_of_each_deferrable_load' in runtimeparams.keys() or 'def_start_timestep' in runtimeparams.keys():
498
+ optim_conf['start_timesteps_of_each_deferrable_load'] = runtimeparams.get(
499
+ 'start_timesteps_of_each_deferrable_load', runtimeparams.get('def_start_timestep'))
500
+ if 'end_timesteps_of_each_deferrable_load' in runtimeparams.keys() or 'def_end_timestep' in runtimeparams.keys():
501
+ optim_conf['end_timesteps_of_each_deferrable_load'] = runtimeparams.get(
502
+ 'end_timesteps_of_each_deferrable_load', runtimeparams.get('def_end_timestep'))
491
503
  if "def_current_state" in runtimeparams.keys():
492
504
  optim_conf["def_current_state"] = [
493
505
  bool(s) for s in runtimeparams["def_current_state"]]
494
- if 'treat_deferrable_load_as_semi_cont' in runtimeparams.keys():
495
- optim_conf['treat_deferrable_load_as_semi_cont'] = [
496
- ast.literal_eval(str(k).capitalize())
497
- for k in runtimeparams['treat_deferrable_load_as_semi_cont']
498
- ]
499
- if 'treat_def_as_semi_cont' in runtimeparams.keys():
506
+ if 'treat_deferrable_load_as_semi_cont' in runtimeparams.keys() or 'treat_def_as_semi_cont' in runtimeparams.keys():
500
507
  optim_conf['treat_deferrable_load_as_semi_cont'] = [
501
- ast.literal_eval(str(k).capitalize())
502
- for k in runtimeparams['treat_def_as_semi_cont']
503
- ]
504
- if 'set_deferrable_load_single_constant' in runtimeparams.keys():
508
+ ast.literal_eval(str(k).capitalize()) for k in runtimeparams.get('treat_deferrable_load_as_semi_cont',runtimeparams.get('treat_def_as_semi_cont'))
509
+ ]
510
+ if 'set_deferrable_load_single_constant' in runtimeparams.keys() or 'set_def_constant' in runtimeparams.keys():
505
511
  optim_conf['set_deferrable_load_single_constant'] = [
506
- ast.literal_eval(str(k).capitalize()) for k in runtimeparams['set_deferrable_load_single_constant']
512
+ ast.literal_eval(str(k).capitalize()) for k in runtimeparams.get('set_deferrable_load_single_constant',runtimeparams.get('set_def_constant'))
507
513
  ]
508
- if 'set_def_constant' in runtimeparams.keys():
509
- optim_conf['set_deferrable_load_single_constant'] = [
510
- ast.literal_eval(str(k).capitalize()) for k in runtimeparams['set_def_constant']
511
- ]
512
- if 'set_deferrable_startup_penalty' in runtimeparams.keys():
513
- optim_conf['set_deferrable_startup_penalty'] = [
514
- ast.literal_eval(str(k).capitalize()) for k in runtimeparams['set_deferrable_startup_penalty']
515
- ]
516
- if 'def_start_penalty' in runtimeparams.keys():
514
+ if 'set_deferrable_startup_penalty' in runtimeparams.keys() or 'def_start_penalty' in runtimeparams.keys():
517
515
  optim_conf['set_deferrable_startup_penalty'] = [
518
- ast.literal_eval(str(k).capitalize()) for k in runtimeparams['def_start_penalty']
516
+ ast.literal_eval(str(k).capitalize()) for k in runtimeparams.get('set_deferrable_startup_penalty',runtimeparams.get('def_start_penalty'))
519
517
  ]
520
518
  if 'def_load_config' in runtimeparams:
521
519
  optim_conf["def_load_config"] = runtimeparams['def_load_config']
@@ -527,9 +525,9 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
527
525
  optim_conf['weight_battery_charge'] = runtimeparams['weight_battery_charge']
528
526
 
529
527
  # Treat retrieve data from Home Assistant (retrieve_hass_conf) configuration parameters passed at runtime
530
- if 'optimization_time_step' in runtimeparams.keys():
531
- retrieve_hass_conf['optimization_time_step'] = pd.to_timedelta(
532
- runtimeparams['optimization_time_step'], "minutes")
528
+ if 'optimization_time_step' in runtimeparams.keys() or 'freq' in runtimeparams.keys():
529
+ retrieve_hass_conf['optimization_time_step'] = pd.to_timedelta(runtimeparams.get(
530
+ 'optimization_time_step', runtimeparams.get('freq')), "minutes")
533
531
  if 'continual_publish' in runtimeparams.keys():
534
532
  retrieve_hass_conf['continual_publish'] = bool(
535
533
  runtimeparams['continual_publish'])
@@ -646,9 +644,7 @@ def get_yaml_parse(params: str, logger: logging.Logger) -> Tuple[dict, dict, dic
646
644
  return False, False, False
647
645
 
648
646
  optim_conf = input_conf.get("optim_conf", {})
649
-
650
647
  retrieve_hass_conf = input_conf.get("retrieve_hass_conf", {})
651
-
652
648
  plant_conf = input_conf.get("plant_conf", {})
653
649
 
654
650
  # Format time parameters
@@ -661,6 +657,70 @@ def get_yaml_parse(params: str, logger: logging.Logger) -> Tuple[dict, dict, dic
661
657
 
662
658
  return retrieve_hass_conf, optim_conf, plant_conf
663
659
 
660
+ def get_legacy_yaml_parse(emhass_conf: dict, use_secrets: Optional[bool] = True,
661
+ params: Optional[str] = None) -> Tuple[dict, dict, dict]:
662
+ """
663
+ Perform parsing of the config.yaml file.
664
+
665
+ :param emhass_conf: Dictionary containing the needed emhass paths
666
+ :type emhass_conf: dict
667
+ :param use_secrets: Indicate if we should use a secrets file or not.
668
+ Set to False for unit tests.
669
+ :type use_secrets: bool, optional
670
+ :param params: Configuration parameters passed from data/options.json
671
+ :type params: str
672
+ :return: A tuple with the dictionaries containing the parsed data
673
+ :rtype: tuple(dict)
674
+
675
+ """
676
+ if params is None:
677
+ with open(emhass_conf["config_path"], 'r') as file:
678
+ input_conf = yaml.load(file, Loader=yaml.FullLoader)
679
+ else:
680
+ input_conf = json.loads(params)
681
+ if use_secrets:
682
+ if params is None:
683
+ with open(emhass_conf["config_path"].parent / 'secrets_emhass.yaml', 'r') as file: # Assume secrets and config file paths are the same
684
+ input_secrets = yaml.load(file, Loader=yaml.FullLoader)
685
+ else:
686
+ input_secrets = input_conf.pop("params_secrets", None)
687
+
688
+ if type(input_conf["retrieve_hass_conf"]) == list: # if using old config version
689
+ retrieve_hass_conf = dict(
690
+ {key: d[key] for d in input_conf["retrieve_hass_conf"] for key in d}
691
+ )
692
+ else:
693
+ retrieve_hass_conf = input_conf.get("retrieve_hass_conf", {})
694
+
695
+ if use_secrets:
696
+ retrieve_hass_conf.update(input_secrets)
697
+ else:
698
+ retrieve_hass_conf["hass_url"] = "http://supervisor/core/api"
699
+ retrieve_hass_conf["long_lived_token"] = "${SUPERVISOR_TOKEN}"
700
+ retrieve_hass_conf["time_zone"] = "Europe/Paris"
701
+ retrieve_hass_conf["lat"] = 45.83
702
+ retrieve_hass_conf["lon"] = 6.86
703
+ retrieve_hass_conf["alt"] = 4807.8
704
+ retrieve_hass_conf["freq"] = pd.to_timedelta(retrieve_hass_conf["freq"], "minutes")
705
+ retrieve_hass_conf["time_zone"] = pytz.timezone(retrieve_hass_conf["time_zone"])
706
+
707
+ if type(input_conf["optim_conf"]) == list:
708
+ optim_conf = dict({key: d[key] for d in input_conf["optim_conf"] for key in d})
709
+ else:
710
+ optim_conf = input_conf.get("optim_conf", {})
711
+
712
+ optim_conf["list_hp_periods"] = dict(
713
+ (key, d[key]) for d in optim_conf["list_hp_periods"] for key in d
714
+ )
715
+ optim_conf["delta_forecast"] = pd.Timedelta(days=optim_conf["delta_forecast"])
716
+
717
+ if type(input_conf["plant_conf"]) == list:
718
+ plant_conf = dict({key: d[key] for d in input_conf["plant_conf"] for key in d})
719
+ else:
720
+ plant_conf = input_conf.get("plant_conf", {})
721
+
722
+ return retrieve_hass_conf, optim_conf, plant_conf
723
+
664
724
 
665
725
  def get_injection_dict(df: pd.DataFrame, plot_size: Optional[int] = 1366) -> dict:
666
726
  """
@@ -843,7 +903,7 @@ def build_config(emhass_conf: dict, logger: logging.Logger, defaults_path: str,
843
903
  logger.info("Obtaining parameters from config.json:")
844
904
  config.update(json.load(data))
845
905
  else:
846
- logger.info("config.json does not exist, or has not been passed")
906
+ logger.info("config.json does not exist, or has not been passed. config parameters may default to config_defaults.json")
847
907
  logger.info("you may like to generate the config.json file on the configuration page")
848
908
 
849
909
  # Check to see if legacy config_emhass.yaml was provided (default /app/config_emhass.yaml)
@@ -853,14 +913,14 @@ def build_config(emhass_conf: dict, logger: logging.Logger, defaults_path: str,
853
913
  legacy_config = yaml.load(data, Loader=yaml.FullLoader)
854
914
  legacy_config_parameters = build_legacy_config_params(emhass_conf,legacy_config,logger)
855
915
  if type(legacy_config_parameters) is not bool:
856
- logger.info("Obtaining parameters from config_emhass.yaml:")
916
+ logger.info("Obtaining parameters from config_emhass.yaml: (will overwrite config parameters)")
857
917
  config.update(legacy_config_parameters)
858
918
 
859
919
  return config
860
920
 
861
921
 
862
922
  def build_legacy_config_params(emhass_conf: dict, legacy_config: dict,
863
- logger: logging.Logger) -> dict:
923
+ logger: logging.Logger) -> dict:
864
924
  """
865
925
  Build a config dictionary with legacy config_emhass.yaml file.
866
926
  Uses the associations file to convert parameter naming conventions (to config.json/config_defaults.json).
@@ -911,8 +971,7 @@ def build_legacy_config_params(emhass_conf: dict, legacy_config: dict,
911
971
  return config
912
972
  # params['associations_dict'] = associations_dict
913
973
 
914
- def param_to_config(param: dict,
915
- logger: logging.Logger) -> dict:
974
+ def param_to_config(param: dict, logger: logging.Logger) -> dict:
916
975
  """
917
976
  A function that extracts the parameters from param back to the config.json format.
918
977
  Extracts parameters from config catagories.
@@ -942,7 +1001,7 @@ def param_to_config(param: dict,
942
1001
  return return_config
943
1002
 
944
1003
  def build_secrets(emhass_conf: dict, logger: logging.Logger, argument: Optional[dict] = {}, options_path: Optional[str] = None,
945
- secrets_path: Optional[str] = None, no_response: Optional[bool] = False) -> Tuple[dict, dict]:
1004
+ secrets_path: Optional[str] = None, no_response: Optional[bool] = False) -> Tuple[dict, dict]:
946
1005
  """
947
1006
  Retrieve and build parameters from secrets locations (ENV, ARG, Secrets file (secrets_emhass.yaml/options.json) and/or Home Assistant (via API))
948
1007
  priority order (lwo to high) = Defaults (written in function), ENV, Options json file, Home Assistant API, Secrets yaml file, Arguments
@@ -271,7 +271,6 @@ def parameter_set():
271
271
  else:
272
272
  return make_response(["Unable to save config file"],500)
273
273
  request_data
274
- app.logger.info(params)
275
274
 
276
275
  # Save params with updated config
277
276
  if os.path.exists(emhass_conf['data_path']):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: emhass
3
- Version: 0.11.0
3
+ Version: 0.11.2
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
@@ -47,9 +47,6 @@ Requires-Dist: plotly>=5.6.0
47
47
  </a>
48
48
  <a style="text-decoration:none" href="https://github.com/davidusb-geek/emhass/actions">
49
49
  <img alt="EMHASS GitHub Workflow Status" src="https://github.com/davidusb-geek/emhass/actions/workflows/publish_docker.yaml/badge.svg?event=release">
50
- </a>
51
- <a style="text-decoration:none" href="https://github.com/davidusb-geek/emhass-add-on/actions">
52
- <img alt="EMHASS-Add-on GitHub Workflow Status" src="https://github.com/davidusb-geek/emhass-add-on/actions/workflows/publish_docker.yaml/badge.svg?event=release">
53
50
  </a>
54
51
  <a hstyle="text-decoration:none" ref="https://codecov.io/github/davidusb-geek/emhass" >
55
52
  <img src="https://codecov.io/github/davidusb-geek/emhass/branch/master/graph/badge.svg?token=BW7KSCHN90"/>
@@ -164,7 +161,7 @@ _Note: Both EMHASS via Docker and EMHASS-Add-on contain the same Docker image. T
164
161
  You can also install EMHASS using Docker as a container. This can be in the same machine as Home Assistant (if your running Home Assistant as a Docker container) or in a different distant machine. To install first pull the latest image:
165
162
  ```bash
166
163
  # pull Docker image
167
- docker docker pull ghcr.io/davidusb-geek/emhass:latest
164
+ docker pull ghcr.io/davidusb-geek/emhass:latest
168
165
  # run Docker image, mounting config.json and secrets_emhass.yaml from host
169
166
  docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v ./config.json:/share/config.json -v ./secrets_emhass.yaml:/app/secrets_emhass.yaml ghcr.io/davidusb-geek/emhass:latest
170
167
  ```
@@ -173,7 +170,7 @@ docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v ./
173
170
  You can also build your image locally. For this clone this repository, and build the image from the Dockerfile:
174
171
  ```bash
175
172
  # git clone EMHASS repo
176
- git clone docker pull ghcr.io/geoderp/emhass:v0.21.3
173
+ git clone https://github.com/davidusb-geek/emhass.git
177
174
  # move to EMHASS directory
178
175
  cd emhass
179
176
  # build Docker image
@@ -34,10 +34,16 @@ class TestRetrieveHass(unittest.TestCase):
34
34
  save_data_to_file = False
35
35
 
36
36
  # Build params with default secrets (no config)
37
- if emhass_conf['defaults_path'].exists():
38
- _,secrets = utils.build_secrets(emhass_conf,logger,no_response=True)
39
- params = utils.build_params(emhass_conf,secrets,{},logger)
40
- retrieve_hass_conf, _, _ = get_yaml_parse(params,logger)
37
+ if emhass_conf['defaults_path'].exists():
38
+ if get_data_from_file:
39
+ _,secrets = utils.build_secrets(emhass_conf,logger,no_response=True)
40
+ params = utils.build_params(emhass_conf,secrets,{},logger)
41
+ retrieve_hass_conf, _, _ = get_yaml_parse(params,logger)
42
+ else:
43
+ emhass_conf['secrets_path'] = root / 'secrets_emhass.yaml'
44
+ emhass_conf['config_path'] = pathlib.Path(root) / 'config_emhass.yaml'
45
+ retrieve_hass_conf, _, _ = utils.get_legacy_yaml_parse(emhass_conf)
46
+ params = None
41
47
  else:
42
48
  raise Exception("config_defaults. does not exist in path: "+str(emhass_conf['defaults_path'] ))
43
49
 
@@ -60,8 +66,8 @@ class TestRetrieveHass(unittest.TestCase):
60
66
  self.rh.df_final, self.days_list, self.var_list = pickle.load(inp)
61
67
  # Else obtain sensor values from HA
62
68
  else:
63
- self.days_list = get_days_list(self.retrieve_hass_conf['historic_days_to_retrieve'])
64
- self.var_list = [self.retrieve_hass_conf['sensor_power_load_no_var_loads'], self.retrieve_hass_conf['sensor_power_photovoltaics']]
69
+ self.days_list = get_days_list(self.retrieve_hass_conf['days_to_retrieve'])
70
+ self.var_list = [self.retrieve_hass_conf['var_PV'], self.retrieve_hass_conf['var_load']]
65
71
  self.rh.get_data(self.days_list, self.var_list,
66
72
  minimal_response=False, significant_changes_only=False)
67
73
  # Check to save updated data to file
@@ -67,7 +67,7 @@ class TestCommandLineUtils(unittest.TestCase):
67
67
  self.assertTrue(params['optim_conf']['lp_solver'] == "default")
68
68
  self.assertTrue(params['optim_conf']['lp_solver_path'] == "empty")
69
69
  self.assertTrue(config['load_peak_hour_periods'] == {'period_hp_1': [{'start': '02:54'}, {'end': '15:24'}], 'period_hp_2': [{'start': '17:24'}, {'end': '20:24'}]})
70
- self.assertTrue(params['retrieve_hass_conf']['sensor_replace_zero'] == ['sensor.power_photovoltaics','sensor.power_load_no_var_loads'])
70
+ self.assertTrue(params['retrieve_hass_conf']['sensor_replace_zero'] == ['sensor.power_photovoltaics'])
71
71
  # Test with config.json
72
72
  config = utils.build_config(emhass_conf,logger,emhass_conf['defaults_path'],emhass_conf['config_path'])
73
73
  params = utils.build_params(emhass_conf,{},config,logger)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes