emhass 0.13.2__py3-none-any.whl → 0.13.3__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/data/associations.csv +2 -1
- emhass/data/config_defaults.json +1 -0
- emhass/optimization.py +54 -48
- emhass/retrieve_hass.py +5 -5
- emhass/static/data/param_definitions.json +9 -2
- {emhass-0.13.2.dist-info → emhass-0.13.3.dist-info}/METADATA +3 -2
- {emhass-0.13.2.dist-info → emhass-0.13.3.dist-info}/RECORD +10 -10
- {emhass-0.13.2.dist-info → emhass-0.13.3.dist-info}/WHEEL +0 -0
- {emhass-0.13.2.dist-info → emhass-0.13.3.dist-info}/entry_points.txt +0 -0
- {emhass-0.13.2.dist-info → emhass-0.13.3.dist-info}/licenses/LICENSE +0 -0
emhass/data/associations.csv
CHANGED
@@ -39,6 +39,7 @@ optim_conf,set_total_pv_sell,set_total_pv_sell
|
|
39
39
|
optim_conf,lp_solver,lp_solver
|
40
40
|
optim_conf,lp_solver_path,lp_solver_path
|
41
41
|
optim_conf,lp_solver_timeout,lp_solver_timeout
|
42
|
+
optim_conf,num_threads,num_threads
|
42
43
|
optim_conf,set_nocharge_from_grid,set_nocharge_from_grid
|
43
44
|
optim_conf,set_nodischarge_to_grid,set_nodischarge_to_grid
|
44
45
|
optim_conf,set_battery_dynamic,set_battery_dynamic
|
@@ -68,4 +69,4 @@ plant_conf,eta_ch,battery_charge_efficiency
|
|
68
69
|
plant_conf,Enom,battery_nominal_energy_capacity
|
69
70
|
plant_conf,SOCmin,battery_minimum_state_of_charge
|
70
71
|
plant_conf,SOCmax,battery_maximum_state_of_charge
|
71
|
-
plant_conf,SOCtarget,battery_target_state_of_charge
|
72
|
+
plant_conf,SOCtarget,battery_target_state_of_charge
|
emhass/data/config_defaults.json
CHANGED
emhass/optimization.py
CHANGED
@@ -3,13 +3,14 @@
|
|
3
3
|
import bz2
|
4
4
|
import copy
|
5
5
|
import logging
|
6
|
+
import os
|
6
7
|
import pickle as cPickle
|
7
8
|
from math import ceil
|
8
9
|
|
9
10
|
import numpy as np
|
10
11
|
import pandas as pd
|
11
12
|
import pulp as plp
|
12
|
-
from pulp import COIN_CMD, GLPK_CMD, PULP_CBC_CMD
|
13
|
+
from pulp import COIN_CMD, GLPK_CMD, PULP_CBC_CMD, HiGHS
|
13
14
|
|
14
15
|
|
15
16
|
class Optimization:
|
@@ -87,6 +88,13 @@ class Optimization:
|
|
87
88
|
self.var_load_cost = var_load_cost
|
88
89
|
self.var_prod_price = var_prod_price
|
89
90
|
self.optim_status = None
|
91
|
+
if "num_threads" in optim_conf.keys():
|
92
|
+
if optim_conf["num_threads"] == 0:
|
93
|
+
self.num_threads = int(os.cpu_count())
|
94
|
+
else:
|
95
|
+
self.num_threads = int(optim_conf["num_threads"])
|
96
|
+
else:
|
97
|
+
self.num_threads = int(os.cpu_count())
|
90
98
|
if "lp_solver" in optim_conf.keys():
|
91
99
|
self.lp_solver = optim_conf["lp_solver"]
|
92
100
|
else:
|
@@ -110,6 +118,7 @@ class Optimization:
|
|
110
118
|
self.logger.debug(f"Optimization configuration: {optim_conf}")
|
111
119
|
self.logger.debug(f"Plant configuration: {plant_conf}")
|
112
120
|
self.logger.debug(f"Solver configuration: lp_solver={self.lp_solver}, lp_solver_path={self.lp_solver_path}")
|
121
|
+
self.logger.debug(f"Number of threads: {self.num_threads}")
|
113
122
|
|
114
123
|
def perform_optimization(
|
115
124
|
self,
|
@@ -642,50 +651,48 @@ class Optimization:
|
|
642
651
|
self.logger.debug(f"Load {k}: Sequence-based constraints set.")
|
643
652
|
|
644
653
|
# --- Thermal deferrable load logic first ---
|
645
|
-
elif (
|
654
|
+
elif (
|
655
|
+
"def_load_config" in self.optim_conf.keys()
|
646
656
|
and len(self.optim_conf["def_load_config"]) > k
|
647
|
-
and "thermal_config" in self.optim_conf["def_load_config"][k]
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
heating_rate
|
677
|
-
* self.timeStep
|
678
|
-
/ self.optim_conf["nominal_power_of_deferrable_loads"][k]
|
679
|
-
)
|
657
|
+
and "thermal_config" in self.optim_conf["def_load_config"][k]
|
658
|
+
):
|
659
|
+
self.logger.debug(f"Load {k} is a thermal deferrable load.")
|
660
|
+
def_load_config = self.optim_conf["def_load_config"][k]
|
661
|
+
if def_load_config and "thermal_config" in def_load_config:
|
662
|
+
hc = def_load_config["thermal_config"]
|
663
|
+
start_temperature = hc["start_temperature"]
|
664
|
+
cooling_constant = hc["cooling_constant"]
|
665
|
+
heating_rate = hc["heating_rate"]
|
666
|
+
overshoot_temperature = hc["overshoot_temperature"]
|
667
|
+
outdoor_temperature_forecast = data_opt["outdoor_temperature_forecast"]
|
668
|
+
desired_temperatures = hc["desired_temperatures"]
|
669
|
+
sense = hc.get("sense", "heat")
|
670
|
+
sense_coeff = 1 if sense == "heat" else -1
|
671
|
+
|
672
|
+
self.logger.debug(f"Load {k}: Thermal parameters: start_temperature={start_temperature}, cooling_constant={cooling_constant}, heating_rate={heating_rate}, overshoot_temperature={overshoot_temperature}")
|
673
|
+
|
674
|
+
predicted_temp = [start_temperature]
|
675
|
+
for Id in set_I:
|
676
|
+
if Id == 0:
|
677
|
+
continue
|
678
|
+
predicted_temp.append(
|
679
|
+
predicted_temp[Id - 1]
|
680
|
+
+ (
|
681
|
+
P_deferrable[k][Id - 1]
|
682
|
+
* (
|
683
|
+
heating_rate
|
684
|
+
* self.timeStep
|
685
|
+
/ self.optim_conf["nominal_power_of_deferrable_loads"][k]
|
680
686
|
)
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
+
)
|
688
|
+
- (
|
689
|
+
cooling_constant
|
690
|
+
* (
|
691
|
+
predicted_temp[Id - 1]
|
692
|
+
- outdoor_temperature_forecast.iloc[Id - 1]
|
687
693
|
)
|
688
694
|
)
|
695
|
+
)
|
689
696
|
|
690
697
|
is_overshoot = plp.LpVariable(
|
691
698
|
f"defload_{k}_overshoot_{Id}"
|
@@ -1135,16 +1142,18 @@ class Optimization:
|
|
1135
1142
|
timeout = self.optim_conf["lp_solver_timeout"]
|
1136
1143
|
# solving with default solver CBC
|
1137
1144
|
if self.lp_solver == "PULP_CBC_CMD":
|
1138
|
-
opt_model.solve(PULP_CBC_CMD(msg=0, timeLimit=timeout, threads=
|
1145
|
+
opt_model.solve(PULP_CBC_CMD(msg=0, timeLimit=timeout, threads=self.num_threads))
|
1139
1146
|
elif self.lp_solver == "GLPK_CMD":
|
1140
|
-
opt_model.solve(GLPK_CMD(msg=0, timeLimit=timeout
|
1147
|
+
opt_model.solve(GLPK_CMD(msg=0, timeLimit=timeout))
|
1148
|
+
elif self.lp_solver == "HiGHS":
|
1149
|
+
opt_model.solve(HiGHS(msg=0, timeLimit=timeout))
|
1141
1150
|
elif self.lp_solver == "COIN_CMD":
|
1142
1151
|
opt_model.solve(
|
1143
|
-
COIN_CMD(msg=0, path=self.lp_solver_path, timeLimit=timeout, threads=
|
1152
|
+
COIN_CMD(msg=0, path=self.lp_solver_path, timeLimit=timeout, threads=self.num_threads)
|
1144
1153
|
)
|
1145
1154
|
else:
|
1146
1155
|
self.logger.warning("Solver %s unknown, using default", self.lp_solver)
|
1147
|
-
opt_model.solve(PULP_CBC_CMD(msg=0, timeLimit=timeout, threads=
|
1156
|
+
opt_model.solve(PULP_CBC_CMD(msg=0, timeLimit=timeout, threads=self.num_threads))
|
1148
1157
|
|
1149
1158
|
# The status of the solution is printed to the screen
|
1150
1159
|
self.optim_status = plp.LpStatus[opt_model.status]
|
@@ -1322,9 +1331,6 @@ class Optimization:
|
|
1322
1331
|
# Solver execution logging
|
1323
1332
|
self.logger.debug(f"Solver selected: {self.lp_solver}")
|
1324
1333
|
self.logger.info(f"Optimization status: {self.optim_status}")
|
1325
|
-
|
1326
|
-
# Results logging
|
1327
|
-
|
1328
1334
|
return opt_tp
|
1329
1335
|
|
1330
1336
|
def perform_perfect_forecast_optim(
|
emhass/retrieve_hass.py
CHANGED
@@ -102,14 +102,14 @@ class RetrieveHass:
|
|
102
102
|
try:
|
103
103
|
response_config = get(url, headers=headers)
|
104
104
|
except Exception:
|
105
|
-
self.logger.error("Unable to access Home
|
105
|
+
self.logger.error("Unable to access Home Assistant instance, check URL")
|
106
106
|
self.logger.error("If using addon, try setting url and token to 'empty'")
|
107
107
|
return False
|
108
108
|
|
109
109
|
try:
|
110
110
|
self.ha_config = response_config.json()
|
111
111
|
except Exception:
|
112
|
-
self.logger.error("EMHASS was unable to obtain configuration data from
|
112
|
+
self.logger.error("EMHASS was unable to obtain configuration data from Home Assistant")
|
113
113
|
return False
|
114
114
|
|
115
115
|
def get_data(
|
@@ -188,7 +188,7 @@ class RetrieveHass:
|
|
188
188
|
response = get(url, headers=headers)
|
189
189
|
except Exception:
|
190
190
|
self.logger.error(
|
191
|
-
"Unable to access Home
|
191
|
+
"Unable to access Home Assistant instance, check URL"
|
192
192
|
)
|
193
193
|
self.logger.error(
|
194
194
|
"If using addon, try setting url and token to 'empty'"
|
@@ -197,7 +197,7 @@ class RetrieveHass:
|
|
197
197
|
else:
|
198
198
|
if response.status_code == 401:
|
199
199
|
self.logger.error(
|
200
|
-
"Unable to access Home
|
200
|
+
"Unable to access Home Assistant instance, TOKEN/KEY"
|
201
201
|
)
|
202
202
|
self.logger.error(
|
203
203
|
"If using addon, try setting url and token to 'empty'"
|
@@ -217,7 +217,7 @@ class RetrieveHass:
|
|
217
217
|
self.logger.error(
|
218
218
|
"The retrieved JSON is empty, A sensor:"
|
219
219
|
+ var
|
220
|
-
+ " may have 0 days of history, passed sensor may not be correct, or days to retrieve is set too
|
220
|
+
+ " may have 0 days of history, passed sensor may not be correct, or days to retrieve is set too high. Check your Logger configuration, ensuring the sensors are in the include list."
|
221
221
|
)
|
222
222
|
else:
|
223
223
|
self.logger.error(
|
@@ -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.",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: emhass
|
3
|
-
Version: 0.13.
|
3
|
+
Version: 0.13.3
|
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
|
@@ -680,7 +681,7 @@ Pull requests are very much accepted on this project. For development, you can f
|
|
680
681
|
|
681
682
|
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
683
|
|
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'.
|
684
|
+
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
685
|
|
685
686
|
|
686
687
|
## License
|
@@ -3,14 +3,14 @@ emhass/command_line.py,sha256=sJIpI11BAJi1Ao7ck4B_zbr6dD1V7VY3S6UmxqRFRcE,75857
|
|
3
3
|
emhass/forecast.py,sha256=lEBKnzJSxpbAYQ-OiW_qnKTTj8cl95yk4Ggkyk_rpyo,83892
|
4
4
|
emhass/machine_learning_forecaster.py,sha256=zr5BczdsvoUCKpxV3XL4Ts-IWcS6zuhXYdeQwBD5gE8,16282
|
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=
|
6
|
+
emhass/optimization.py,sha256=5Fb9Wn3nIi_IqnKV8XX-fxpG7eRxsiunaW-ZvXYmQ5M,68260
|
7
|
+
emhass/retrieve_hass.py,sha256=JQqNurTHtu6VdTTBnh0Sx1N3KMOLLuAn6jMQLokgpcw,29532
|
8
8
|
emhass/utils.py,sha256=UysLZJ-TzsWvkNyihXkPwcf_qGUpxN_HfnAVcb0KWkY,73889
|
9
9
|
emhass/web_server.py,sha256=xOgM3lVwXAN6Ctoi5in21ImeSvx6-wDNtgYZqLQN01I,29153
|
10
|
-
emhass/data/associations.csv,sha256=
|
10
|
+
emhass/data/associations.csv,sha256=J2HTMK5UjdU6k8ehxkPAsnXzl7-gATKGWXed_0oO2To,4180
|
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=Bz-qXLCWmKzvr6q_N4yVbgHua_K6OhL_Y--GFXjIx0c,3148
|
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=vsmc1Ykeng2fjRy5PhxOvWCBtgKLr17sCOsGpMkpkLQ,22000
|
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.3.dist-info/METADATA,sha256=ZzGJGH85Ru7-TpPuqzXcJcTPXu7h2R7qhxEf-8BIDBk,51534
|
29
|
+
emhass-0.13.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
30
|
+
emhass-0.13.3.dist-info/entry_points.txt,sha256=6Bp1NFOGNv_fSTxYl1ke3K3h3aqAcBxI-bgq5yq-i1M,52
|
31
|
+
emhass-0.13.3.dist-info/licenses/LICENSE,sha256=1X3-S1yvOCBDBeox1aK3dq00m7dA8NDtcPrpKPISzbE,1077
|
32
|
+
emhass-0.13.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|