emhass 0.5.1__py3-none-any.whl → 0.6.1__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 -6
- emhass/forecast.py +2 -1
- emhass/optimization.py +21 -16
- emhass/retrieve_hass.py +22 -12
- emhass/utils.py +3 -0
- emhass/web_server.py +8 -5
- emhass-0.6.1.dist-info/LICENSE +9 -0
- {emhass-0.5.1.dist-info → emhass-0.6.1.dist-info}/METADATA +28 -11
- emhass-0.6.1.dist-info/RECORD +16 -0
- {emhass-0.5.1.dist-info → emhass-0.6.1.dist-info}/WHEEL +1 -1
- {emhass-0.5.1.dist-info → emhass-0.6.1.dist-info}/entry_points.txt +0 -1
- emhass-0.5.1.dist-info/RECORD +0 -15
- {emhass-0.5.1.dist-info → emhass-0.6.1.dist-info}/top_level.txt +0 -0
emhass/command_line.py
CHANGED
@@ -488,7 +488,7 @@ def publish_data(input_data_dict: dict, logger: logging.Logger,
|
|
488
488
|
custom_deferrable_forecast_id = params['passed_data']['custom_deferrable_forecast_id']
|
489
489
|
for k in range(input_data_dict['opt'].optim_conf['num_def_loads']):
|
490
490
|
if "P_deferrable{}".format(k) not in opt_res_latest.columns:
|
491
|
-
logger.error("P_deferrable{}".format(k)+" was not found in results DataFrame. Optimization task may need to be relaunched or it did not
|
491
|
+
logger.error("P_deferrable{}".format(k)+" was not found in results DataFrame. Optimization task may need to be relaunched or it did not converge to a solution.")
|
492
492
|
else:
|
493
493
|
input_data_dict['rh'].post_data(opt_res_latest["P_deferrable{}".format(k)], idx_closest,
|
494
494
|
custom_deferrable_forecast_id[k]["entity_id"],
|
@@ -500,7 +500,7 @@ def publish_data(input_data_dict: dict, logger: logging.Logger,
|
|
500
500
|
# Publish battery power
|
501
501
|
if input_data_dict['opt'].optim_conf['set_use_battery']:
|
502
502
|
if 'P_batt' not in opt_res_latest.columns:
|
503
|
-
logger.error("P_batt was not found in results DataFrame. Optimization task may need to be relaunched or it did not
|
503
|
+
logger.error("P_batt was not found in results DataFrame. Optimization task may need to be relaunched or it did not converge to a solution.")
|
504
504
|
else:
|
505
505
|
custom_batt_forecast_id = params['passed_data']['custom_batt_forecast_id']
|
506
506
|
input_data_dict['rh'].post_data(opt_res_latest['P_batt'], idx_closest,
|
@@ -536,15 +536,15 @@ def publish_data(input_data_dict: dict, logger: logging.Logger,
|
|
536
536
|
custom_cost_fun_id["friendly_name"],
|
537
537
|
type_var = 'cost_fun',
|
538
538
|
publish_prefix = publish_prefix)
|
539
|
-
# Publish the optimization status
|
540
|
-
'''
|
539
|
+
# Publish the optimization status
|
541
540
|
custom_cost_fun_id = params['passed_data']['custom_optim_status_id']
|
542
|
-
input_data_dict['rh'].post_data(
|
541
|
+
input_data_dict['rh'].post_data(opt_res_latest['optim_status'], idx_closest,
|
543
542
|
custom_cost_fun_id["entity_id"],
|
544
543
|
custom_cost_fun_id["unit_of_measurement"],
|
545
544
|
custom_cost_fun_id["friendly_name"],
|
546
545
|
type_var = 'optim_status',
|
547
|
-
publish_prefix = publish_prefix)
|
546
|
+
publish_prefix = publish_prefix)
|
547
|
+
cols_published = cols_published+["optim_status"]
|
548
548
|
# Publish unit_load_cost
|
549
549
|
custom_unit_load_cost_id = params['passed_data']['custom_unit_load_cost_id']
|
550
550
|
input_data_dict['rh'].post_data(opt_res_latest['unit_load_cost'], idx_closest,
|
emhass/forecast.py
CHANGED
@@ -315,7 +315,8 @@ class forecast(object):
|
|
315
315
|
# Define index
|
316
316
|
data.set_index('ts', inplace=True)
|
317
317
|
else:
|
318
|
-
self.logger.error("
|
318
|
+
self.logger.error("Method %r is not valid", method)
|
319
|
+
data = None
|
319
320
|
return data
|
320
321
|
|
321
322
|
def cloud_cover_to_irradiance(self, cloud_cover: pd.Series,
|
emhass/optimization.py
CHANGED
@@ -204,10 +204,10 @@ class optimization:
|
|
204
204
|
for i in set_I)
|
205
205
|
elif self.costfun == 'cost':
|
206
206
|
if self.optim_conf['set_total_pv_sell']:
|
207
|
-
objective = plp.lpSum(-0.001*self.timeStep*unit_load_cost[i]*
|
207
|
+
objective = plp.lpSum(-0.001*self.timeStep*unit_load_cost[i]*(P_load[i] + P_def_sum[i])
|
208
208
|
for i in set_I)
|
209
209
|
else:
|
210
|
-
objective = plp.lpSum(-0.001*self.timeStep*unit_load_cost[i]*
|
210
|
+
objective = plp.lpSum(-0.001*self.timeStep*unit_load_cost[i]*P_grid_pos[i]
|
211
211
|
for i in set_I)
|
212
212
|
elif self.costfun == 'self-consumption':
|
213
213
|
if type_self_conso == 'bigm':
|
@@ -387,25 +387,24 @@ class optimization:
|
|
387
387
|
|
388
388
|
## Finally, we call the solver to solve our optimization model:
|
389
389
|
# solving with default solver CBC
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
except Exception:
|
400
|
-
self.logger.error("It was not possible to find a valid solver for Pulp package")
|
390
|
+
if self.lp_solver == 'PULP_CBC_CMD':
|
391
|
+
opt_model.solve(PULP_CBC_CMD(msg=0))
|
392
|
+
elif self.lp_solver == 'GLPK_CMD':
|
393
|
+
opt_model.solve(GLPK_CMD(msg=0))
|
394
|
+
elif self.lp_solver == 'COIN_CMD':
|
395
|
+
opt_model.solve(COIN_CMD(msg=0, path=self.lp_solver_path))
|
396
|
+
else:
|
397
|
+
self.logger.warning("Solver %s unknown, using default", self.lp_solver)
|
398
|
+
opt_model.solve()
|
401
399
|
|
402
400
|
# The status of the solution is printed to the screen
|
403
401
|
self.optim_status = plp.LpStatus[opt_model.status]
|
404
402
|
self.logger.info("Status: " + self.optim_status)
|
405
403
|
if plp.value(opt_model.objective) is None:
|
406
|
-
self.logger.warning("Cost function cannot be evaluated
|
404
|
+
self.logger.warning("Cost function cannot be evaluated")
|
405
|
+
return
|
407
406
|
else:
|
408
|
-
self.logger.info("Total value of the Cost function = "
|
407
|
+
self.logger.info("Total value of the Cost function = %.02f", plp.value(opt_model.objective))
|
409
408
|
|
410
409
|
# Build results Dataframe
|
411
410
|
opt_tp = pd.DataFrame()
|
@@ -450,7 +449,10 @@ class optimization:
|
|
450
449
|
opt_tp["cost_fun_profit"] = [-0.001*self.timeStep*(unit_load_cost[i]*P_grid_pos[i].varValue + \
|
451
450
|
unit_prod_price[i]*P_grid_neg[i].varValue) for i in set_I]
|
452
451
|
elif self.costfun == 'cost':
|
453
|
-
|
452
|
+
if self.optim_conf['set_total_pv_sell']:
|
453
|
+
opt_tp["cost_fun_cost"] = [-0.001*self.timeStep*unit_load_cost[i]*(P_load[i] + P_def_sum_tp[i]) for i in set_I]
|
454
|
+
else:
|
455
|
+
opt_tp["cost_fun_cost"] = [-0.001*self.timeStep*unit_load_cost[i]*P_grid_pos[i].varValue for i in set_I]
|
454
456
|
elif self.costfun == 'self-consumption':
|
455
457
|
if type_self_conso == 'maxmin':
|
456
458
|
opt_tp["cost_fun_selfcons"] = [-0.001*self.timeStep*unit_load_cost[i]*SC[i].varValue for i in set_I]
|
@@ -459,6 +461,9 @@ class optimization:
|
|
459
461
|
unit_prod_price[i]*P_grid_neg[i].varValue) for i in set_I]
|
460
462
|
else:
|
461
463
|
self.logger.error("The cost function specified type is not valid")
|
464
|
+
|
465
|
+
# Add the optimization status
|
466
|
+
opt_tp["optim_status"] = self.optim_status
|
462
467
|
|
463
468
|
return opt_tp
|
464
469
|
|
emhass/retrieve_hass.py
CHANGED
@@ -116,6 +116,9 @@ class retrieve_hass:
|
|
116
116
|
response = get(url, headers=headers)
|
117
117
|
except Exception:
|
118
118
|
return "Request Get Error"
|
119
|
+
else:
|
120
|
+
if response.status_code > 299:
|
121
|
+
return f"Request Get Error: {response.status_code}"
|
119
122
|
'''import bz2 # Uncomment to save a serialized data for tests
|
120
123
|
import _pickle as cPickle
|
121
124
|
with bz2.BZ2File("data/test_response_get_data_get_method.pbz2", "w") as f:
|
@@ -131,8 +134,8 @@ class retrieve_hass:
|
|
131
134
|
self.logger.error("Retrieved empty Dataframe, check that correct day or variable names are passed")
|
132
135
|
self.logger.error("Either the names of the passed variables are not correct or days_to_retrieve is larger than the recorded history of your sensor (check your recorder settings)")
|
133
136
|
if i == 0: # Defining the DataFrame container
|
134
|
-
from_date = pd.to_datetime(df_raw['last_changed']).min()
|
135
|
-
to_date = pd.to_datetime(df_raw['last_changed']).max()
|
137
|
+
from_date = pd.to_datetime(df_raw['last_changed'], format="ISO8601").min()
|
138
|
+
to_date = pd.to_datetime(df_raw['last_changed'], format="ISO8601").max()
|
136
139
|
ts = pd.to_datetime(pd.date_range(start=from_date, end=to_date, freq=self.freq),
|
137
140
|
format='%Y-%d-%m %H:%M').round(self.freq)
|
138
141
|
df_day = pd.DataFrame(index = ts)
|
@@ -140,7 +143,7 @@ class retrieve_hass:
|
|
140
143
|
df_tp = df_raw.copy()[['state']].replace(
|
141
144
|
['unknown', 'unavailable', ''], np.nan).astype(float).rename(columns={'state': var})
|
142
145
|
# Setting index, resampling and concatenation
|
143
|
-
df_tp.set_index(pd.to_datetime(df_raw['last_changed']), inplace=True)
|
146
|
+
df_tp.set_index(pd.to_datetime(df_raw['last_changed'], format="ISO8601"), inplace=True)
|
144
147
|
df_tp = df_tp.resample(self.freq).mean()
|
145
148
|
df_day = pd.concat([df_day, df_tp], axis=1)
|
146
149
|
|
@@ -174,17 +177,14 @@ class retrieve_hass:
|
|
174
177
|
:rtype: pandas.DataFrame
|
175
178
|
|
176
179
|
"""
|
177
|
-
|
178
|
-
|
180
|
+
try:
|
181
|
+
if load_negative: # Apply the correct sign to load power
|
179
182
|
self.df_final[var_load+'_positive'] = -self.df_final[var_load]
|
180
|
-
|
181
|
-
self.logger.error("Variable "+var_load+" was not found. This is typically because no data could be retrieved from Home Assistant")
|
182
|
-
else:
|
183
|
-
try:
|
183
|
+
else:
|
184
184
|
self.df_final[var_load+'_positive'] = self.df_final[var_load]
|
185
|
-
|
186
|
-
|
187
|
-
|
185
|
+
self.df_final.drop([var_load], inplace=True, axis=1)
|
186
|
+
except KeyError:
|
187
|
+
self.logger.error("Variable "+var_load+" was not found. This is typically because no data could be retrieved from Home Assistant")
|
188
188
|
if set_zero_min: # Apply minimum values
|
189
189
|
self.df_final.clip(lower=0.0, inplace=True, axis=1)
|
190
190
|
self.df_final.replace(to_replace=0.0, value=np.nan, inplace=True)
|
@@ -282,6 +282,8 @@ class retrieve_hass:
|
|
282
282
|
state = np.round(data_df.sum()[0],2)
|
283
283
|
elif type_var == 'unit_load_cost' or type_var == 'unit_prod_price':
|
284
284
|
state = np.round(data_df.loc[data_df.index[idx]],4)
|
285
|
+
elif type_var == 'optim_status':
|
286
|
+
state = data_df.loc[data_df.index[idx]]
|
285
287
|
else:
|
286
288
|
state = np.round(data_df.loc[data_df.index[idx]],2)
|
287
289
|
if type_var == 'power':
|
@@ -305,6 +307,14 @@ class retrieve_hass:
|
|
305
307
|
elif type_var == 'mlforecaster':
|
306
308
|
data = retrieve_hass.get_attr_data_dict(data_df, idx, entity_id, unit_of_measurement,
|
307
309
|
friendly_name, "scheduled_forecast", state)
|
310
|
+
elif type_var == 'optim_status':
|
311
|
+
data = {
|
312
|
+
"state": state,
|
313
|
+
"attributes": {
|
314
|
+
"unit_of_measurement": unit_of_measurement,
|
315
|
+
"friendly_name": friendly_name
|
316
|
+
}
|
317
|
+
}
|
308
318
|
else:
|
309
319
|
data = {
|
310
320
|
"state": "{:.2f}".format(state),
|
emhass/utils.py
CHANGED
@@ -136,6 +136,7 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
|
|
136
136
|
'custom_batt_soc_forecast_id': {"entity_id": "sensor.soc_batt_forecast", "unit_of_measurement": "%", "friendly_name": "Battery SOC Forecast"},
|
137
137
|
'custom_grid_forecast_id': {"entity_id": "sensor.p_grid_forecast", "unit_of_measurement": "W", "friendly_name": "Grid Power Forecast"},
|
138
138
|
'custom_cost_fun_id': {"entity_id": "sensor.total_cost_fun_value", "unit_of_measurement": "", "friendly_name": "Total cost function value"},
|
139
|
+
'custom_optim_status_id': {"entity_id": "sensor.optim_status", "unit_of_measurement": "", "friendly_name": "EMHASS optimization status"},
|
139
140
|
'custom_unit_load_cost_id': {"entity_id": "sensor.unit_load_cost", "unit_of_measurement": "€/kWh", "friendly_name": "Unit Load Cost"},
|
140
141
|
'custom_unit_prod_price_id': {"entity_id": "sensor.unit_prod_price", "unit_of_measurement": "€/kWh", "friendly_name": "Unit Prod Price"},
|
141
142
|
'custom_deferrable_forecast_id': custom_deferrable_forecast_id,
|
@@ -339,6 +340,8 @@ def treat_runtimeparams(runtimeparams: str, params: str, retrieve_hass_conf: dic
|
|
339
340
|
params['passed_data']['custom_grid_forecast_id'] = runtimeparams['custom_grid_forecast_id']
|
340
341
|
if 'custom_cost_fun_id' in runtimeparams.keys():
|
341
342
|
params['passed_data']['custom_cost_fun_id'] = runtimeparams['custom_cost_fun_id']
|
343
|
+
if 'custom_optim_status_id' in runtimeparams.keys():
|
344
|
+
params['passed_data']['custom_optim_status_id'] = runtimeparams['custom_optim_status_id']
|
342
345
|
if 'custom_unit_load_cost_id' in runtimeparams.keys():
|
343
346
|
params['passed_data']['custom_unit_load_cost_id'] = runtimeparams['custom_unit_load_cost_id']
|
344
347
|
if 'custom_unit_prod_price_id' in runtimeparams.keys():
|
emhass/web_server.py
CHANGED
@@ -5,7 +5,7 @@ from flask import Flask, request, make_response, render_template
|
|
5
5
|
from jinja2 import Environment, PackageLoader
|
6
6
|
from requests import get
|
7
7
|
from waitress import serve
|
8
|
-
from importlib.metadata import version
|
8
|
+
from importlib.metadata import version, PackageNotFoundError
|
9
9
|
from pathlib import Path
|
10
10
|
import os, json, argparse, pickle, yaml, logging
|
11
11
|
from distutils.util import strtobool
|
@@ -265,9 +265,9 @@ if __name__ == "__main__":
|
|
265
265
|
app.logger.error("options.json does not exists")
|
266
266
|
DATA_PATH = "/share/" #"/data/"
|
267
267
|
else:
|
268
|
-
CONFIG_PATH = "/app/config_emhass.yaml"
|
268
|
+
CONFIG_PATH = os.getenv("CONFIG_PATH", default="/app/config_emhass.yaml")
|
269
269
|
options = None
|
270
|
-
DATA_PATH = "/app/data/"
|
270
|
+
DATA_PATH = os.getenv("DATA_PATH", default="/app/data/")
|
271
271
|
config_path = Path(CONFIG_PATH)
|
272
272
|
data_path = Path(DATA_PATH)
|
273
273
|
|
@@ -328,7 +328,7 @@ if __name__ == "__main__":
|
|
328
328
|
else:
|
329
329
|
costfun = os.getenv('LOCAL_COSTFUN', default='profit')
|
330
330
|
logging_level = os.getenv('LOGGING_LEVEL', default='INFO')
|
331
|
-
with open('/app/secrets_emhass.yaml', 'r') as file:
|
331
|
+
with open(os.getenv('SECRETS_PATH', default='/app/secrets_emhass.yaml'), 'r') as file:
|
332
332
|
params_secrets = yaml.load(file, Loader=yaml.FullLoader)
|
333
333
|
hass_url = params_secrets['hass_url']
|
334
334
|
|
@@ -364,5 +364,8 @@ if __name__ == "__main__":
|
|
364
364
|
app.logger.info("Launching the emhass webserver at: http://"+web_ui_url+":"+str(port))
|
365
365
|
app.logger.info("Home Assistant data fetch will be performed using url: "+hass_url)
|
366
366
|
app.logger.info("The data path is: "+str(data_path))
|
367
|
-
|
367
|
+
try:
|
368
|
+
app.logger.info("Using core emhass version: "+version('emhass'))
|
369
|
+
except PackageNotFoundError:
|
370
|
+
app.logger.info("Using development emhass version")
|
368
371
|
serve(app, host=web_ui_url, port=port, threads=8)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021-2022 David HERNANDEZ
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -1,25 +1,24 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: emhass
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.6.1
|
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
|
7
7
|
Author-email: davidusb@gmail.com
|
8
|
-
License: UNKNOWN
|
9
8
|
Keywords: energy,management,optimization,hass
|
10
|
-
Platform: UNKNOWN
|
11
9
|
Classifier: Development Status :: 5 - Production/Stable
|
12
10
|
Classifier: Intended Audience :: Developers
|
13
11
|
Classifier: Topic :: Software Development :: Build Tools
|
14
12
|
Classifier: License :: OSI Approved :: MIT License
|
15
13
|
Classifier: Programming Language :: Python :: 3.10
|
16
14
|
Classifier: Operating System :: OS Independent
|
17
|
-
Requires-Python: >=3.9,
|
15
|
+
Requires-Python: >=3.9, <3.12
|
18
16
|
Description-Content-Type: text/markdown
|
17
|
+
License-File: LICENSE
|
19
18
|
Requires-Dist: wheel
|
20
|
-
Requires-Dist: numpy ==1.
|
21
|
-
Requires-Dist: scipy
|
22
|
-
Requires-Dist: pandas ==
|
19
|
+
Requires-Dist: numpy ==1.26
|
20
|
+
Requires-Dist: scipy ==1.11.3
|
21
|
+
Requires-Dist: pandas ==2.0.3
|
23
22
|
Requires-Dist: pvlib >=0.10.1
|
24
23
|
Requires-Dist: protobuf >=3.0.0
|
25
24
|
Requires-Dist: pytz >=2021.1
|
@@ -27,8 +26,8 @@ Requires-Dist: requests >=2.25.1
|
|
27
26
|
Requires-Dist: beautifulsoup4 >=4.9.3
|
28
27
|
Requires-Dist: pulp >=2.4
|
29
28
|
Requires-Dist: pyyaml >=5.4.1
|
30
|
-
Requires-Dist: tables ==3.
|
31
|
-
Requires-Dist: skforecast ==0.
|
29
|
+
Requires-Dist: tables ==3.9.1
|
30
|
+
Requires-Dist: skforecast ==0.11.0
|
32
31
|
|
33
32
|
<div align="center">
|
34
33
|
<br>
|
@@ -324,6 +323,26 @@ shell_command:
|
|
324
323
|
```
|
325
324
|
And you should be careful that the list of dictionaries has the correct length, which is the number of defined deferrable loads.
|
326
325
|
|
326
|
+
### Computed variables and published data
|
327
|
+
|
328
|
+
Below you can find a list of the variables resulting from EMHASS computation, showed in the charts, and then published to Home Assistant through the ```publish_data``` command:
|
329
|
+
|
330
|
+
| EMHASS variable | Definition | Home Assistant published sensor |
|
331
|
+
| --------------- | ---------- | --------------------------------|
|
332
|
+
| P_PV | Forecasted power generation from your solar panels (Watts). This helps you predict how much solar energy you will produce during the forecast period. | sensor.p_pv_forecast |
|
333
|
+
| P_Load | Forecasted household power consumption (Watts). This gives you an idea of how much energy your appliances are expected to use. | sensor.p_load_forecast |
|
334
|
+
| P_deferrableX [X = 0, 1, 2, ...] | Forecasted power consumption of deferrable loads (Watts). Deferable loads are appliances that can be managed by EMHASS. EMHASS helps you optimise energy usage by prioritising solar self-consumption and minimizing reliance on the grid or by taking advantage or supply and feed-in tariff volatility. You can have multiple deferable loads and you use this sensor in HA to control these loads via smart switch or other IoT means at your disposal. | sensor.p_deferrableX |
|
335
|
+
| P_grid_pos | Forecasted power imported from the grid (Watts). This indicates the amount of energy you are expected to draw from the grid when your solar production is insufficient to meet your needs or it is advantagous to consume from the grid. | - |
|
336
|
+
| P_grid_neg | Forecasted power exported to the grid (Watts). This indicates the amount of excess solar energy you are expected to send back to the grid during the forecast period. | - |
|
337
|
+
| P_batt | Forecasted (dis)charge power load (Watt) for the battery (if installed). If negative it indicates the battery is charging, if positive that the battery is discharging. | sensor.p_batt_forecast |
|
338
|
+
| P_grid | Forecasted net power flow between your home and the grid (Watts). This is calculated as P_grid_pos - P_grid_neg. A positive value indicates net export, while a negative value indicates net import. | sensor.p_grid_forecast |
|
339
|
+
| SOC_opt | Forecasted battery optimized Status Of Charge (SOC) percentage level | sensor.soc_batt_forecast |
|
340
|
+
| unit_load_cost | Forecasted cost per unit of energy you pay to the grid (typically "Currency"/kWh). This helps you understand the expected energy cost during the forecast period. | sensor.unit_load_cost |
|
341
|
+
| unit_prod_price | Forecasted price you receive for selling excess solar energy back to the grid (typically "Currency"/kWh). This helps you understand the potential income from your solar production. | sensor.unit_prod_price |
|
342
|
+
| cost_profit | Forecasted profit or loss from your energy usage for the forecast period. This is calculated as unit_load_cost * P_Load - unit_prod_price * P_grid_pos. A positive value indicates a profit, while a negative value indicates a loss. | sensor.total_cost_profit_value |
|
343
|
+
| cost_fun_cost | Forecasted cost associated with deferring loads to maximize solar self-consumption. This helps you evaluate the trade-off between managing the load and not managing and potential cost savings. | sensor.total_cost_fun_value |
|
344
|
+
|
345
|
+
|
327
346
|
## Passing your own data
|
328
347
|
|
329
348
|
In EMHASS we have basically 4 forecasts to deal with:
|
@@ -448,5 +467,3 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
448
467
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
449
468
|
|
450
469
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
451
|
-
|
452
|
-
|
@@ -0,0 +1,16 @@
|
|
1
|
+
emhass/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
emhass/command_line.py,sha256=GCFwCVZ1sZkjGfLMJW8Ub_r1RIXnsLzjJ1l2IifYze4,36634
|
3
|
+
emhass/forecast.py,sha256=WTlsoan_IgzRKqm5Ri1mHOC2sLdlTv4kauNY03n8EPQ,43088
|
4
|
+
emhass/machine_learning_forecaster.py,sha256=Zhs8zcTn9E5yBNjdyYDPXjd2x6iQuqtL-QgaE74ODco,15633
|
5
|
+
emhass/optimization.py,sha256=Bj7m9UJr0kFqbMFsGAALw41ly1I7x8Jl_er4IYH5xfg,31481
|
6
|
+
emhass/retrieve_hass.py,sha256=4RqpwJDTyqTlyg6mez5yu3rx7sm2od5lrkCMzb8Db4k,17345
|
7
|
+
emhass/utils.py,sha256=SCDRHRG3JNcfSzm2S23DBJEzOuzaYGUhwKzzgqGtIYE,24057
|
8
|
+
emhass/web_server.py,sha256=jhNKo4Mi62g9-aNPzCQtGXbcPjDxC_Is_rvwJ97f1yg,20190
|
9
|
+
emhass/static/style.css,sha256=5qGJl0MZGSaSvr0HUcGQ9UgMtDKP2CiyE-1H-jbsLuU,6760
|
10
|
+
emhass/templates/index.html,sha256=6zXvh8KGZC3wUmyW5RrkMn1PsQkWDI2wtrbLv7VUPmc,3224
|
11
|
+
emhass-0.6.1.dist-info/LICENSE,sha256=1X3-S1yvOCBDBeox1aK3dq00m7dA8NDtcPrpKPISzbE,1077
|
12
|
+
emhass-0.6.1.dist-info/METADATA,sha256=ryVijY824OZ-rYzENbfwxqUtw1yA-R6OyVf-weluEC4,32059
|
13
|
+
emhass-0.6.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
14
|
+
emhass-0.6.1.dist-info/entry_points.txt,sha256=6Bp1NFOGNv_fSTxYl1ke3K3h3aqAcBxI-bgq5yq-i1M,52
|
15
|
+
emhass-0.6.1.dist-info/top_level.txt,sha256=L7fIX4awfmxQbAePtSdVg2e6x_HhghfReHfsKSpKr9I,7
|
16
|
+
emhass-0.6.1.dist-info/RECORD,,
|
emhass-0.5.1.dist-info/RECORD
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
emhass/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
emhass/command_line.py,sha256=59vdgjJ4sPDEnWlD2cehaLSrBU6E3qtvaVfmBvcisS0,36657
|
3
|
-
emhass/forecast.py,sha256=3zqFIKwfu5cjGGLDGUZ-j1eMu8sWlSAc679oGmIwPeo,43060
|
4
|
-
emhass/machine_learning_forecaster.py,sha256=Zhs8zcTn9E5yBNjdyYDPXjd2x6iQuqtL-QgaE74ODco,15633
|
5
|
-
emhass/optimization.py,sha256=FURVCVt0lWSIX2DYF8GEGu8FuwqL4kp42hS7uVO8ekQ,31299
|
6
|
-
emhass/retrieve_hass.py,sha256=TRECIc_ZohY1MOVC5t7UjB0t9Ke3gNIn6x8ZrnBMGUo,16969
|
7
|
-
emhass/utils.py,sha256=F8b_8SHAjQDCDgk0w1WS6b3oQKdEUXkhiNSe2_9ufGs,23729
|
8
|
-
emhass/web_server.py,sha256=KFrsvHLeM3ShCUpnZx_InysJ194My3HHGoVc5cS1zo4,19961
|
9
|
-
emhass/static/style.css,sha256=5qGJl0MZGSaSvr0HUcGQ9UgMtDKP2CiyE-1H-jbsLuU,6760
|
10
|
-
emhass/templates/index.html,sha256=6zXvh8KGZC3wUmyW5RrkMn1PsQkWDI2wtrbLv7VUPmc,3224
|
11
|
-
emhass-0.5.1.dist-info/METADATA,sha256=eoUSC-ioYEfxP01Lbq9fOXScDIegvDrLLc0sTrz6gEw,28913
|
12
|
-
emhass-0.5.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
13
|
-
emhass-0.5.1.dist-info/entry_points.txt,sha256=tJULCm7mHGYb_IxyzPN_4KFYvhxv209_jq68jPiByy0,53
|
14
|
-
emhass-0.5.1.dist-info/top_level.txt,sha256=L7fIX4awfmxQbAePtSdVg2e6x_HhghfReHfsKSpKr9I,7
|
15
|
-
emhass-0.5.1.dist-info/RECORD,,
|
File without changes
|