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.
@@ -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
@@ -10,6 +10,7 @@
10
10
  "lp_solver": "default",
11
11
  "lp_solver_path": "empty",
12
12
  "lp_solver_timeout": 45,
13
+ "num_threads": 0,
13
14
  "set_nocharge_from_grid": false,
14
15
  "set_nodischarge_to_grid": true,
15
16
  "set_battery_dynamic": false,
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 ("def_load_config" in self.optim_conf.keys()
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
- self.logger.debug(f"Load {k} is a thermal deferrable load.")
651
-
652
- # Special case of a thermal deferrable load
653
- def_load_config = self.optim_conf["def_load_config"][k]
654
- if def_load_config and "thermal_config" in def_load_config:
655
- hc = def_load_config["thermal_config"]
656
- start_temperature = hc["start_temperature"]
657
- cooling_constant = hc["cooling_constant"]
658
- heating_rate = hc["heating_rate"]
659
- overshoot_temperature = hc["overshoot_temperature"]
660
- outdoor_temperature_forecast = data_opt["outdoor_temperature_forecast"]
661
- desired_temperatures = hc["desired_temperatures"]
662
- sense = hc.get("sense", "heat")
663
- sense_coeff = 1 if sense == "heat" else -1
664
-
665
- self.logger.debug(f"Load {k}: Thermal parameters: start_temperature={start_temperature}, cooling_constant={cooling_constant}, heating_rate={heating_rate}, overshoot_temperature={overshoot_temperature}")
666
-
667
- predicted_temp = [start_temperature]
668
- for Id in set_I:
669
- if Id == 0:
670
- continue
671
- predicted_temp.append(
672
- predicted_temp[Id - 1]
673
- + (
674
- P_deferrable[k][Id - 1]
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
- cooling_constant
683
- * (
684
- predicted_temp[Id - 1]
685
- - outdoor_temperature_forecast.iloc[Id - 1]
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=7))
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, threads=7))
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=7)
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=7))
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 Assistance instance, check URL")
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 HA")
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 Assistance instance, check URL"
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 Assistance instance, TOKEN/KEY"
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 heigh"
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 and ‘COIN_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.2
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=_uKG7QKVtdpbv4U4Y_R9xLdWR17lB4YQWAuuONqk8ng,67947
7
- emhass/retrieve_hass.py,sha256=KZQ6esRYq0Typp7yyZUwEdYKHFPqDmnsuVxuDhBdWc0,29444
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=pFbL3LosAhWc4SmHWZGQa-2ykwmFAqhV1nmd4hTYNKU,4144
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=J1hgdA6D9mliPABbSaGqdO4rG3Xr5-9nI-jIWqrRylA,3128
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=GAXsnpdg9F5zbl7McdUoiiAfjwjRgQ24sYhovEv9sEI,21692
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.2.dist-info/METADATA,sha256=iJQCKI1fRrYBS4VFqomje9VAXF_-tZ6Fy09kCa2MFno,51493
29
- emhass-0.13.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
- emhass-0.13.2.dist-info/entry_points.txt,sha256=6Bp1NFOGNv_fSTxYl1ke3K3h3aqAcBxI-bgq5yq-i1M,52
31
- emhass-0.13.2.dist-info/licenses/LICENSE,sha256=1X3-S1yvOCBDBeox1aK3dq00m7dA8NDtcPrpKPISzbE,1077
32
- emhass-0.13.2.dist-info/RECORD,,
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,,