emhass 0.13.2__py3-none-any.whl → 0.13.4__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 +6 -2
- emhass/data/associations.csv +6 -1
- emhass/data/config_defaults.json +5 -0
- emhass/forecast.py +52 -12
- emhass/machine_learning_forecaster.py +0 -2
- emhass/optimization.py +262 -126
- emhass/retrieve_hass.py +11 -9
- emhass/static/data/param_definitions.json +39 -2
- emhass/utils.py +61 -21
- emhass/web_server.py +10 -28
- {emhass-0.13.2.dist-info → emhass-0.13.4.dist-info}/METADATA +47 -23
- {emhass-0.13.2.dist-info → emhass-0.13.4.dist-info}/RECORD +15 -15
- {emhass-0.13.2.dist-info → emhass-0.13.4.dist-info}/WHEEL +0 -0
- {emhass-0.13.2.dist-info → emhass-0.13.4.dist-info}/entry_points.txt +0 -0
- {emhass-0.13.2.dist-info → emhass-0.13.4.dist-info}/licenses/LICENSE +0 -0
@@ -122,13 +122,14 @@
|
|
122
122
|
},
|
123
123
|
"lp_solver": {
|
124
124
|
"friendly_name": "Linear programming solver",
|
125
|
-
"Description": "Set the name of the linear programming solver that will be used. Defaults to ‘COIN_CMD’. The options are ‘PULP_CBC_CMD’, ‘GLPK_CMD
|
125
|
+
"Description": "Set the name of the linear programming solver that will be used. Defaults to ‘COIN_CMD’. The options are ‘PULP_CBC_CMD’, ‘GLPK_CMD’, ‘HiGHS’, and ‘COIN_CMD’.",
|
126
126
|
"input": "select",
|
127
127
|
"select_options": [
|
128
128
|
"default",
|
129
129
|
"COIN_CMD",
|
130
130
|
"PULP_CBC_CMD",
|
131
|
-
"GLPK_CMD"
|
131
|
+
"GLPK_CMD",
|
132
|
+
"HiGHS"
|
132
133
|
],
|
133
134
|
"default_value": "COIN_CMD"
|
134
135
|
},
|
@@ -138,6 +139,12 @@
|
|
138
139
|
"input": "text",
|
139
140
|
"default_value": "/usr/bin/cbc"
|
140
141
|
},
|
142
|
+
"num_threads": {
|
143
|
+
"friendly_name": "Number of threads to use for the LP solver",
|
144
|
+
"Description": "Set the number of threads for the LP solver to use, when supported by the solver. Defaults to 0 (autodetect)",
|
145
|
+
"input": "int",
|
146
|
+
"default_value": 0
|
147
|
+
},
|
141
148
|
"lp_solver_timeout": {
|
142
149
|
"friendly_name": "Linear programming solver timeout",
|
143
150
|
"Description": "Set the maximum time (in seconds) for the LP solver. Defaults to 45.",
|
@@ -180,6 +187,30 @@
|
|
180
187
|
"input": "boolean",
|
181
188
|
"default_value": false
|
182
189
|
},
|
190
|
+
"inverter_ac_output_max": {
|
191
|
+
"friendly_name": "Max hybrid inverter AC output power",
|
192
|
+
"Description": "Maximum hybrid inverter output power from combined PV and battery discharge.",
|
193
|
+
"input": "int",
|
194
|
+
"default_value": 0
|
195
|
+
},
|
196
|
+
"inverter_ac_input_max": {
|
197
|
+
"friendly_name": "Max hybrid inverter AC input power",
|
198
|
+
"Description": "Maximum hybrid inverter input power from grid to charge battery.",
|
199
|
+
"input": "int",
|
200
|
+
"default_value": 0
|
201
|
+
},
|
202
|
+
"inverter_efficiency_dc_ac": {
|
203
|
+
"friendly_name": "Hybrid inverter efficency DC to AC",
|
204
|
+
"Description": "Hybrid inverter efficiency from the DC bus to AC output. (percentage/100)",
|
205
|
+
"input": "float",
|
206
|
+
"default_value": 1.0
|
207
|
+
},
|
208
|
+
"inverter_efficiency_ac_dc": {
|
209
|
+
"friendly_name": "Hybrid inverter efficency AC to DC",
|
210
|
+
"Description": "Hybrid inverter efficiency when charging from the AC input to DC bus. (percentage/100)",
|
211
|
+
"input": "float",
|
212
|
+
"default_value": 1.0
|
213
|
+
},
|
183
214
|
"compute_curtailment": {
|
184
215
|
"friendly_name": "Set compute curtailment (grid export limit)",
|
185
216
|
"Description": "Set to True to compute a special PV curtailment variable (Default False)",
|
@@ -477,6 +508,12 @@
|
|
477
508
|
"Description": "The desired battery state of charge at the end of each optimization cycle. (percentage/100)",
|
478
509
|
"input": "float",
|
479
510
|
"default_value": 0.6
|
511
|
+
},
|
512
|
+
"ignore_pv_feedback_during_curtailment": {
|
513
|
+
"friendly_name": "Ignore PV feedback during curtailment",
|
514
|
+
"Description": "When set to true, prevents PV forecast from being updated with real PV data, avoiding flip-flop behavior during curtailment operations",
|
515
|
+
"input": "bool",
|
516
|
+
"default_value": false
|
480
517
|
}
|
481
518
|
}
|
482
519
|
}
|
emhass/utils.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
1
|
from __future__ import annotations
|
3
2
|
|
4
3
|
import ast
|
@@ -120,12 +119,12 @@ def get_forecast_dates(
|
|
120
119
|
|
121
120
|
"""
|
122
121
|
freq = pd.to_timedelta(freq, "minutes")
|
123
|
-
start_forecast =
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
microsecond=0
|
122
|
+
start_forecast = (
|
123
|
+
pd.Timestamp(datetime.now(), tz=time_zone)
|
124
|
+
.replace(microsecond=0)
|
125
|
+
.floor(freq=freq)
|
128
126
|
)
|
127
|
+
end_forecast = start_forecast + pd.Timedelta(days=delta_forecast)
|
129
128
|
forecast_dates = (
|
130
129
|
pd.date_range(
|
131
130
|
start=start_forecast,
|
@@ -409,11 +408,29 @@ def treat_runtimeparams(
|
|
409
408
|
runtimeparams.get("delta_forecast_daily", None) is not None
|
410
409
|
or runtimeparams.get("delta_forecast", None) is not None
|
411
410
|
):
|
412
|
-
delta_forecast
|
413
|
-
|
414
|
-
|
411
|
+
# Use old param name delta_forecast (if provided) for backwards compatibility
|
412
|
+
delta_forecast = runtimeparams.get("delta_forecast", None)
|
413
|
+
# Prefer new param name delta_forecast_daily
|
414
|
+
delta_forecast = runtimeparams.get("delta_forecast_daily", delta_forecast)
|
415
|
+
# Ensure delta_forecast is numeric and at least 1 day
|
416
|
+
if delta_forecast is None:
|
417
|
+
logger.warning("delta_forecast_daily is missing so defaulting to 1 day")
|
418
|
+
delta_forecast = 1
|
419
|
+
else:
|
420
|
+
try:
|
421
|
+
delta_forecast = int(delta_forecast)
|
422
|
+
except ValueError:
|
423
|
+
logger.warning(
|
424
|
+
"Invalid delta_forecast_daily value (%s) so defaulting to 1 day",
|
425
|
+
delta_forecast,
|
426
|
+
)
|
427
|
+
delta_forecast = 1
|
428
|
+
if delta_forecast <= 0:
|
429
|
+
logger.warning(
|
430
|
+
"delta_forecast_daily is too low (%s) so defaulting to 1 day",
|
431
|
+
delta_forecast,
|
415
432
|
)
|
416
|
-
|
433
|
+
delta_forecast = 1
|
417
434
|
params["optim_conf"]["delta_forecast_daily"] = pd.Timedelta(
|
418
435
|
days=delta_forecast
|
419
436
|
)
|
@@ -574,27 +591,49 @@ def treat_runtimeparams(
|
|
574
591
|
# Loop forecasts, check if value is a list and greater than or equal to forecast_dates
|
575
592
|
for method, forecast_key in enumerate(list_forecast_key):
|
576
593
|
if forecast_key in runtimeparams.keys():
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
594
|
+
forecast_input = runtimeparams[forecast_key]
|
595
|
+
if isinstance(forecast_input, dict):
|
596
|
+
forecast_data_df = pd.DataFrame.from_dict(
|
597
|
+
forecast_input, orient="index"
|
598
|
+
).reset_index()
|
599
|
+
forecast_data_df.columns = ["time", "value"]
|
600
|
+
forecast_data_df["time"] = pd.to_datetime(
|
601
|
+
forecast_data_df["time"], format="ISO8601", utc=True
|
602
|
+
).dt.tz_convert(time_zone)
|
603
|
+
|
604
|
+
# align index with forecast_dates
|
605
|
+
forecast_data_df = (
|
606
|
+
forecast_data_df.resample(
|
607
|
+
pd.to_timedelta(optimization_time_step, "minutes"),
|
608
|
+
on="time",
|
609
|
+
)
|
610
|
+
.aggregate({"value": "mean"})
|
611
|
+
.reindex(forecast_dates, method="nearest")
|
612
|
+
)
|
613
|
+
forecast_data_df["value"] = (
|
614
|
+
forecast_data_df["value"].ffill().bfill()
|
615
|
+
)
|
616
|
+
forecast_input = forecast_data_df["value"].tolist()
|
617
|
+
if isinstance(forecast_input, list) and len(forecast_input) >= len(
|
618
|
+
forecast_dates
|
619
|
+
):
|
620
|
+
params["passed_data"][forecast_key] = forecast_input
|
581
621
|
params["optim_conf"][forecast_methods[method]] = "list"
|
582
622
|
else:
|
583
623
|
logger.error(
|
584
|
-
f"ERROR: The passed data is either
|
624
|
+
f"ERROR: The passed data is either the wrong type or the length is not correct, length should be {str(len(forecast_dates))}"
|
585
625
|
)
|
586
626
|
logger.error(
|
587
627
|
f"Passed type is {str(type(runtimeparams[forecast_key]))} and length is {str(len(runtimeparams[forecast_key]))}"
|
588
628
|
)
|
589
629
|
# Check if string contains list, if so extract
|
590
|
-
if isinstance(
|
591
|
-
if isinstance(ast.literal_eval(
|
592
|
-
|
593
|
-
|
594
|
-
)
|
630
|
+
if isinstance(forecast_input, str):
|
631
|
+
if isinstance(ast.literal_eval(forecast_input), list):
|
632
|
+
forecast_input = ast.literal_eval(forecast_input)
|
633
|
+
runtimeparams[forecast_key] = forecast_input
|
595
634
|
list_non_digits = [
|
596
635
|
x
|
597
|
-
for x in
|
636
|
+
for x in forecast_input
|
598
637
|
if not (isinstance(x, int) or isinstance(x, float))
|
599
638
|
]
|
600
639
|
if len(list_non_digits) > 0:
|
@@ -1638,6 +1677,7 @@ def build_params(
|
|
1638
1677
|
"end_timesteps_of_each_deferrable_load": None,
|
1639
1678
|
"alpha": None,
|
1640
1679
|
"beta": None,
|
1680
|
+
"ignore_pv_feedback_during_curtailment": None,
|
1641
1681
|
}
|
1642
1682
|
|
1643
1683
|
return params
|
emhass/web_server.py
CHANGED
@@ -10,10 +10,10 @@ import threading
|
|
10
10
|
from importlib.metadata import PackageNotFoundError, version
|
11
11
|
from pathlib import Path
|
12
12
|
|
13
|
+
import jinja2
|
13
14
|
import yaml
|
14
15
|
from flask import Flask, make_response, request
|
15
16
|
from flask import logging as log
|
16
|
-
from jinja2 import Environment, PackageLoader
|
17
17
|
from waitress import serve
|
18
18
|
|
19
19
|
from emhass.command_line import (
|
@@ -50,6 +50,10 @@ params_secrets = {}
|
|
50
50
|
continual_publish_thread = []
|
51
51
|
injection_dict = {}
|
52
52
|
|
53
|
+
templates = jinja2.Environment(
|
54
|
+
loader=jinja2.PackageLoader("emhass", "templates"),
|
55
|
+
)
|
56
|
+
|
53
57
|
|
54
58
|
def create_app(settings_override=None):
|
55
59
|
"""
|
@@ -137,14 +141,6 @@ def index():
|
|
137
141
|
|
138
142
|
"""
|
139
143
|
app.logger.info("EMHASS server online, serving index.html...")
|
140
|
-
# Load HTML template
|
141
|
-
file_loader = PackageLoader("emhass", "templates")
|
142
|
-
env = Environment(loader=file_loader)
|
143
|
-
# check if index.html exists
|
144
|
-
if "index.html" not in env.list_templates():
|
145
|
-
app.logger.error("Unable to find index.html in emhass module")
|
146
|
-
return make_response(["ERROR: unable to find index.html in emhass module"], 404)
|
147
|
-
template = env.get_template("index.html")
|
148
144
|
# Load cached dict (if exists), to present generated plot tables
|
149
145
|
if (emhass_conf["data_path"] / "injection_dict.pkl").exists():
|
150
146
|
with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "rb") as fid:
|
@@ -159,6 +155,7 @@ def index():
|
|
159
155
|
# basename = request.headers.get("X-Ingress-Path", "")
|
160
156
|
# return make_response(template.render(injection_dict=injection_dict, basename=basename))
|
161
157
|
|
158
|
+
template = templates.get_template("index.html")
|
162
159
|
return make_response(template.render(injection_dict=injection_dict))
|
163
160
|
|
164
161
|
|
@@ -174,16 +171,8 @@ def configuration():
|
|
174
171
|
if (emhass_conf["data_path"] / "params.pkl").exists():
|
175
172
|
with open(str(emhass_conf["data_path"] / "params.pkl"), "rb") as fid:
|
176
173
|
emhass_conf["config_path"], params = pickle.load(fid)
|
177
|
-
|
178
|
-
|
179
|
-
env = Environment(loader=file_loader)
|
180
|
-
# check if configuration.html exists
|
181
|
-
if "configuration.html" not in env.list_templates():
|
182
|
-
app.logger.error("Unable to find configuration.html in emhass module")
|
183
|
-
return make_response(
|
184
|
-
["ERROR: unable to find configuration.html in emhass module"], 404
|
185
|
-
)
|
186
|
-
template = env.get_template("configuration.html")
|
174
|
+
|
175
|
+
template = templates.get_template("configuration.html")
|
187
176
|
return make_response(template.render(config=params))
|
188
177
|
|
189
178
|
|
@@ -195,15 +184,6 @@ def template_action():
|
|
195
184
|
|
196
185
|
"""
|
197
186
|
app.logger.info(" >> Sending rendered template table data")
|
198
|
-
file_loader = PackageLoader("emhass", "templates")
|
199
|
-
env = Environment(loader=file_loader)
|
200
|
-
# Check if template.html exists
|
201
|
-
if "template.html" not in env.list_templates():
|
202
|
-
app.logger.error("Unable to find template.html in emhass module")
|
203
|
-
return make_response(
|
204
|
-
["WARNING: unable to find template.html in emhass module"], 404
|
205
|
-
)
|
206
|
-
template = env.get_template("template.html")
|
207
187
|
if (emhass_conf["data_path"] / "injection_dict.pkl").exists():
|
208
188
|
with open(str(emhass_conf["data_path"] / "injection_dict.pkl"), "rb") as fid:
|
209
189
|
injection_dict = pickle.load(fid)
|
@@ -211,6 +191,8 @@ def template_action():
|
|
211
191
|
app.logger.warning("Unable to obtain plot data from injection_dict.pkl")
|
212
192
|
app.logger.warning("Try running an launch an optimization task")
|
213
193
|
injection_dict = {}
|
194
|
+
|
195
|
+
template = templates.get_template("template.html")
|
214
196
|
return make_response(template.render(injection_dict=injection_dict))
|
215
197
|
|
216
198
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: emhass
|
3
|
-
Version: 0.13.
|
3
|
+
Version: 0.13.4
|
4
4
|
Summary: An Energy Management System for Home Assistant
|
5
5
|
Project-URL: Homepage, https://github.com/davidusb-geek/emhass
|
6
6
|
Project-URL: Source, https://github.com/davidusb-geek/emhass
|
@@ -24,6 +24,7 @@ Requires-Python: <3.13,>=3.10
|
|
24
24
|
Requires-Dist: flask>=3.1.0
|
25
25
|
Requires-Dist: gunicorn>=23.0.0
|
26
26
|
Requires-Dist: h5py>=3.12.1
|
27
|
+
Requires-Dist: highspy>=1.10.0
|
27
28
|
Requires-Dist: numpy<2.3.0,>=2.0.0
|
28
29
|
Requires-Dist: pandas>=2.2.0
|
29
30
|
Requires-Dist: plotly>=6.0.0
|
@@ -117,17 +118,10 @@ Description-Content-Type: text/markdown
|
|
117
118
|
</div>
|
118
119
|
|
119
120
|
<br>
|
120
|
-
<p align="
|
121
|
-
|
122
|
-
</p>
|
123
|
-
<p align="center">
|
124
|
-
<a href="https://www.buymeacoffee.com/davidusbgeek" target="_blank">
|
125
|
-
<img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" >
|
126
|
-
</a>
|
121
|
+
<p align="left">
|
122
|
+
EMHASS is a Python module designed to optimize your home energy interfacing with Home Assistant.
|
127
123
|
</p>
|
128
124
|
|
129
|
-
EHMASS is a Python module designed to optimize your home energy interfacing with Home Assistant.
|
130
|
-
|
131
125
|
## Introduction
|
132
126
|
|
133
127
|
EMHASS (Energy Management for Home Assistant) is an optimization tool designed for residential households. The package uses a Linear Programming approach to optimize energy usage while considering factors such as electricity prices, power generation from solar panels, and energy storage from batteries. EMHASS provides a high degree of configurability, making it easy to integrate with Home Assistant and other smart home systems. Whether you have solar panels, energy storage, or just a controllable load, EMHASS can provide an optimized daily schedule for your devices, allowing you to save money and minimize your environmental impact.
|
@@ -196,12 +190,13 @@ _Note: Both EMHASS via Docker and EMHASS-Add-on contain the same Docker image. T
|
|
196
190
|
|
197
191
|
### Method 2) Running EMHASS in Docker
|
198
192
|
|
199
|
-
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.
|
193
|
+
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. The "share" folder is where EMHASS stores the config.json file. In the examples below adjust the "-v" volume mappings to reflect where your path to the local host directory needs to be mapped to.
|
194
|
+
To install first pull the latest image:
|
200
195
|
```bash
|
201
196
|
# pull Docker image
|
202
197
|
docker pull ghcr.io/davidusb-geek/emhass:latest
|
203
|
-
# run Docker image, mounting config.json and secrets_emhass.yaml from host
|
204
|
-
docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v
|
198
|
+
# run Docker image, mounting the dir storing config.json and secrets_emhass.yaml from host
|
199
|
+
docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v /emhass/share:/share/ -v /emhass/secrets_emhass.yaml:/app/secrets_emhass.yaml ghcr.io/davidusb-geek/emhass:latest
|
205
200
|
```
|
206
201
|
*Note it is not recommended to install the latest EMHASS image with `:latest` *(as you would likely want to control when you update EMHASS version)*. Instead, find the [latest version tag](https://github.com/davidusb-geek/emhass/pkgs/container/emhass) (E.g: `v0.2.1`) and replace `latest`*
|
207
202
|
|
@@ -215,7 +210,7 @@ cd emhass
|
|
215
210
|
# may need to set architecture tag (docker build --build-arg TARGETARCH=amd64 -t emhass-local .)
|
216
211
|
docker build -t emhass-local .
|
217
212
|
# run built Docker image, mounting config.json and secrets_emhass.yaml from host
|
218
|
-
docker run --rm -it -p 5000:5000 --name emhass-container -v
|
213
|
+
docker run --rm -it -p 5000:5000 --name emhass-container -v /emhass/share:/share -v /emhass/secrets_emhass.yaml:/app/secrets_emhass.yaml emhass-local
|
219
214
|
```
|
220
215
|
|
221
216
|
Before running the docker container, make sure you have a designated folder for emhass on your host device and a `secrets_emhass.yaml` file. You can get a example of the secrets file from [`secrets_emhass(example).yaml`](https://github.com/davidusb-geek/emhass/blob/master/secrets_emhass(example).yaml) file on this repository.
|
@@ -231,23 +226,23 @@ Latitude: 45.83
|
|
231
226
|
Longitude: 6.86
|
232
227
|
Altitude: 4807.8
|
233
228
|
EOT
|
234
|
-
docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v
|
229
|
+
docker run --rm -it --restart always -p 5000:5000 --name emhass-container -v /emhass/share:/share -v /emhass/secrets_emhass.yaml:/app/secrets_emhass.yaml ghcr.io/davidusb-geek/emhass:latest
|
235
230
|
```
|
236
231
|
|
237
232
|
#### Docker, things to note
|
238
233
|
|
239
|
-
- You can create a `config.json` file prior to running emhass. *(obtain a example from: [config_defaults.json](https://github.com/davidusb-geek/emhass/blob/enhass-standalone-addon-merge/src/emhass/data/config_defaults.json)* Alteratively, you can insert your parameters into the configuration page on the EMHASS web server. (for EMHASS to auto create a config.json) With either option, the volume mount `-v
|
234
|
+
- You can create a `config.json` file prior to running emhass. *(obtain a example from: [config_defaults.json](https://github.com/davidusb-geek/emhass/blob/enhass-standalone-addon-merge/src/emhass/data/config_defaults.json)* Alteratively, you can insert your parameters into the configuration page on the EMHASS web server. (for EMHASS to auto create a config.json) With either option, the volume mount `-v /emhass/share:/share` should be applied to make sure your config is stored on the host device. (to be not deleted when the EMHASS container gets removed/image updated)*
|
240
235
|
|
241
236
|
- If you wish to keep a local, semi-persistent copy of the EMHASS-generated data, create a local folder on your device, then mount said folder inside the container.
|
242
237
|
```bash
|
243
238
|
#create data folder
|
244
239
|
mkdir -p ~/emhass/data
|
245
|
-
docker run -it --restart always -p 5000:5000 -e LOCAL_COSTFUN="profit" -v
|
240
|
+
docker run -it --restart always -p 5000:5000 -e LOCAL_COSTFUN="profit" -v /emhass/share:/share -v /emhass/data:/data -v /emhass/secrets_emhass.yaml:/app/secrets_emhass.yaml --name DockerEMHASS <REPOSITORY:TAG>
|
246
241
|
```
|
247
242
|
|
248
243
|
- If you wish to set the web_server's homepage optimization diagrams to a timezone other than UTC, set `TZ` environment variable on docker run:
|
249
244
|
```bash
|
250
|
-
docker run -it --restart always -p 5000:5000 -e TZ="Europe/Paris" -v
|
245
|
+
docker run -it --restart always -p 5000:5000 -e TZ="Europe/Paris" -v /emhass/share:/share -v /emhass/secrets_emhass.yaml:/app/secrets_emhass.yaml --name DockerEMHASS <REPOSITORY:TAG>
|
251
246
|
```
|
252
247
|
### Method 3) Legacy method using a Python virtual environment *(Legacy CLI)*
|
253
248
|
If you wish to run EMHASS optimizations with cli commands. *(no persistent web server session)* you can run EMHASS via the python package alone *(not wrapped in a Docker container)*.
|
@@ -319,12 +314,22 @@ Additional optimization strategies were developed later, that can be used in com
|
|
319
314
|
|
320
315
|
### Dayahead Optimization - Method 1) Add-on and docker standalone
|
321
316
|
|
322
|
-
|
317
|
+
We can use the `shell_command` integration in `configuration.yaml`:
|
323
318
|
```yaml
|
324
319
|
shell_command:
|
325
320
|
dayahead_optim: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://localhost:5000/action/dayahead-optim"
|
326
321
|
publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{}' http://localhost:5000/action/publish-data"
|
327
322
|
```
|
323
|
+
An alternative that will be useful when passing data at runtime (see dedicated section), we can use the the `rest_command` instead:
|
324
|
+
```yaml
|
325
|
+
rest_command:
|
326
|
+
url: http://127.0.0.1:5000/action/dayahead-optim
|
327
|
+
method: POST
|
328
|
+
headers:
|
329
|
+
content-type: application/json
|
330
|
+
payload: >-
|
331
|
+
{}
|
332
|
+
```
|
328
333
|
### Dayahead Optimization - Method 2) Legacy method using a Python virtual environment
|
329
334
|
|
330
335
|
In `configuration.yaml`:
|
@@ -387,8 +392,8 @@ In `automations.yaml`:
|
|
387
392
|
```
|
388
393
|
in configuration page/`config.json`
|
389
394
|
```json
|
390
|
-
|
391
|
-
|
395
|
+
"method_ts_round": "first"
|
396
|
+
"continual_publish": true
|
392
397
|
```
|
393
398
|
In this automation, the day-ahead optimization is performed once a day, every day at 5:30am.
|
394
399
|
If the `optimization_time_step` parameter is set to `30` *(default)* in the configuration, the results of the day-ahead optimization will generate 48 values *(for each entity)*, a value for every 30 minutes in a day *(i.e. 24 hrs x 2)*.
|
@@ -542,7 +547,7 @@ For users who wish to have full control of exactly when they would like to run a
|
|
542
547
|
|
543
548
|
in configuration page/`config.json` :
|
544
549
|
```json
|
545
|
-
|
550
|
+
"continual_publish": false
|
546
551
|
```
|
547
552
|
POST action :
|
548
553
|
```bash
|
@@ -666,6 +671,25 @@ curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0,
|
|
666
671
|
curl -i -H 'Content-Type:application/json' -X POST -d '{"pv_power_forecast":[0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93], "prediction_horizon":10, "soc_init":0.5,"soc_final":0.6,"operating_hours_of_each_deferrable_load":[1,3],"start_timesteps_of_each_deferrable_load":[0,3],"end_timesteps_of_each_deferrable_load":[0,6]}' http://localhost:5000/action/naive-mpc-optim
|
667
672
|
```
|
668
673
|
|
674
|
+
For a more readable option we can use the `rest_command` integration:
|
675
|
+
```yaml
|
676
|
+
rest_command:
|
677
|
+
url: http://127.0.0.1:5000/action/dayahead-optim
|
678
|
+
method: POST
|
679
|
+
headers:
|
680
|
+
content-type: application/json
|
681
|
+
payload: >-
|
682
|
+
{
|
683
|
+
"pv_power_forecast": [0, 70, 141.22, 246.18, 513.5, 753.27, 1049.89, 1797.93, 1697.3, 3078.93],
|
684
|
+
"prediction_horizon":10,
|
685
|
+
"soc_init":0.5,
|
686
|
+
"soc_final":0.6,
|
687
|
+
"operating_hours_of_each_deferrable_load":[1,3],
|
688
|
+
"start_timesteps_of_each_deferrable_load":[0,3],
|
689
|
+
"end_timesteps_of_each_deferrable_load":[0,6]
|
690
|
+
}
|
691
|
+
```
|
692
|
+
|
669
693
|
## A machine learning forecaster
|
670
694
|
|
671
695
|
Starting in v0.4.0 a new machine learning forecaster class was introduced.
|
@@ -680,7 +704,7 @@ Pull requests are very much accepted on this project. For development, you can f
|
|
680
704
|
|
681
705
|
Some problems may arise from solver-related issues in the Pulp package. It was found that for arm64 architectures (ie. Raspberry Pi4, 64 bits) the default solver is not available. A workaround is to use another solver. The `glpk` solver is an option.
|
682
706
|
|
683
|
-
This can be controlled in the configuration file with parameters `lp_solver` and `lp_solver_path`. The options for `lp_solver` are: 'PULP_CBC_CMD', 'GLPK_CMD' and 'COIN_CMD'. If using 'COIN_CMD' as the solver you will need to provide the correct path to this solver in parameter `lp_solver_path`, ex: '/usr/bin/cbc'.
|
707
|
+
This can be controlled in the configuration file with parameters `lp_solver` and `lp_solver_path`. The options for `lp_solver` are: 'PULP_CBC_CMD', 'GLPK_CMD', 'HiGHS', and 'COIN_CMD'. If using 'COIN_CMD' as the solver you will need to provide the correct path to this solver in parameter `lp_solver_path`, ex: '/usr/bin/cbc'.
|
684
708
|
|
685
709
|
|
686
710
|
## License
|
@@ -1,16 +1,16 @@
|
|
1
1
|
emhass/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
emhass/command_line.py,sha256=
|
3
|
-
emhass/forecast.py,sha256=
|
4
|
-
emhass/machine_learning_forecaster.py,sha256=
|
2
|
+
emhass/command_line.py,sha256=XrD4uOONtaCDUQ3TMjJwDDIuOjyO_K8BZH3r_TBN1Eg,76048
|
3
|
+
emhass/forecast.py,sha256=hLau1KjvwHM6jRQtWyhFLMf3fehdHIQlWG7A_inToKI,85951
|
4
|
+
emhass/machine_learning_forecaster.py,sha256=tRC9P94-gMAPJsxWilT_jeful1Xmcev4PfMmuGFdzIk,16258
|
5
5
|
emhass/machine_learning_regressor.py,sha256=Ih1q-vUWHWbGFxv9F12omwgyMRp-iaMU_m-yploVbyU,9532
|
6
|
-
emhass/optimization.py,sha256=
|
7
|
-
emhass/retrieve_hass.py,sha256=
|
8
|
-
emhass/utils.py,sha256=
|
9
|
-
emhass/web_server.py,sha256=
|
10
|
-
emhass/data/associations.csv,sha256=
|
6
|
+
emhass/optimization.py,sha256=iusC3BBbCHDDPiD-QzOq27mv7nernCtuh3kRTwHU0Jw,73793
|
7
|
+
emhass/retrieve_hass.py,sha256=FPWK43NOD8Hq-oLbrJ_M1Sh8h5rOLwfkL1zpRWVBLoE,29593
|
8
|
+
emhass/utils.py,sha256=95IjexSlUQbzkovIUQizzHzXdxQGdYGjeWZy8rwKAvA,75940
|
9
|
+
emhass/web_server.py,sha256=fwdGHPPu3_wJ3bqrQVctK357iW4Gz57-mVSfIluRs88,28075
|
10
|
+
emhass/data/associations.csv,sha256=BIQNjKpr-QC3cMJIRzJ7F2eMGNVubDe0544dYWMIJw4,4418
|
11
11
|
emhass/data/cec_inverters.pbz2,sha256=ca-dO6sv38_FI2w_6fkAIzcrEqzFBkG8MHKNGbCZPow,189400
|
12
12
|
emhass/data/cec_modules.pbz2,sha256=Y639TNqhaIxh2Ec7AUPxy8k4lQugY5rURVVVexj0fMU,1885444
|
13
|
-
emhass/data/config_defaults.json,sha256=
|
13
|
+
emhass/data/config_defaults.json,sha256=9GUT1zjEIMxijbLXRjO4_VAoi_x0tsq2pczAQoHuwoI,3287
|
14
14
|
emhass/img/emhass_icon.png,sha256=Kyx6hXQ1huJLHAq2CaBfjYXR25H9j99PSWHI0lShkaQ,19030
|
15
15
|
emhass/static/advanced.html,sha256=gAhsd14elDwh1Ts4lf9wn_ZkczzzObq5qOimi_la3Ic,2067
|
16
16
|
emhass/static/basic.html,sha256=ro2WwWgJyoUhqx_nJFzKCEG8FA8863vSHLmrjGYcEgs,677
|
@@ -18,15 +18,15 @@ emhass/static/configuration_list.html,sha256=i4v83RVduWjdjkjPhA74e-j8NSUpFzqMGU3
|
|
18
18
|
emhass/static/configuration_script.js,sha256=Ek0Ry1Ae6ZGMl28mYxno6bPTwY4rK7AHcL58C6T6qUo,31727
|
19
19
|
emhass/static/script.js,sha256=-JYS8fHjchrMi1hYYKMd9p7vZvPcnYiY8NNuRC99fJM,16323
|
20
20
|
emhass/static/style.css,sha256=a_8YlGubn1zoF5RTLJ_Qkrb8tAjUY9p7oAKxhCvJY2s,19288
|
21
|
-
emhass/static/data/param_definitions.json,sha256=
|
21
|
+
emhass/static/data/param_definitions.json,sha256=aKqXH4Cd3cucAIG2gs2AADNDsTxipDHY0SZ9lxRAqHQ,23330
|
22
22
|
emhass/static/img/emhass_icon.png,sha256=Kyx6hXQ1huJLHAq2CaBfjYXR25H9j99PSWHI0lShkaQ,19030
|
23
23
|
emhass/static/img/emhass_logo_short.svg,sha256=yzMcqtBRCV8rH84-MwnigZh45_f9Eoqwho9P8nCodJA,66736
|
24
24
|
emhass/static/img/feather-sprite.svg,sha256=VHjMJQg88wXa9CaeYrKGhNtyK0xdd47zCqwSIa-hxo8,60319
|
25
25
|
emhass/templates/configuration.html,sha256=M-_L__juYzcdGDaryGrz6LG2mguW2f1Sx6k01YfG7Dc,2885
|
26
26
|
emhass/templates/index.html,sha256=1V44c0yyliu_z8inl0K-zmmmkhQumH3Bqk8Jj1YJPzY,3076
|
27
27
|
emhass/templates/template.html,sha256=TkGgMecQEbFUZA4ymPwMUzNjKHsENvCgroUWbPt7G4Y,158
|
28
|
-
emhass-0.13.
|
29
|
-
emhass-0.13.
|
30
|
-
emhass-0.13.
|
31
|
-
emhass-0.13.
|
32
|
-
emhass-0.13.
|
28
|
+
emhass-0.13.4.dist-info/METADATA,sha256=HdwXErVla8BAOCAUcAb_Y3KBTzv6RnpP3sFPGu4FupE,52124
|
29
|
+
emhass-0.13.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
30
|
+
emhass-0.13.4.dist-info/entry_points.txt,sha256=6Bp1NFOGNv_fSTxYl1ke3K3h3aqAcBxI-bgq5yq-i1M,52
|
31
|
+
emhass-0.13.4.dist-info/licenses/LICENSE,sha256=1X3-S1yvOCBDBeox1aK3dq00m7dA8NDtcPrpKPISzbE,1077
|
32
|
+
emhass-0.13.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|