emhass 0.10.6__py3-none-any.whl → 0.15.5__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 +1827 -735
- emhass/connection_manager.py +108 -0
- emhass/data/associations.csv +98 -0
- emhass/data/cec_inverters.pbz2 +0 -0
- emhass/data/cec_modules.pbz2 +0 -0
- emhass/data/config_defaults.json +120 -0
- emhass/forecast.py +1482 -622
- emhass/img/emhass_icon.png +0 -0
- emhass/machine_learning_forecaster.py +565 -212
- emhass/machine_learning_regressor.py +162 -122
- emhass/optimization.py +1724 -590
- emhass/retrieve_hass.py +1104 -248
- emhass/static/advanced.html +9 -1
- emhass/static/basic.html +4 -2
- emhass/static/configuration_list.html +48 -0
- emhass/static/configuration_script.js +956 -0
- emhass/static/data/param_definitions.json +592 -0
- emhass/static/script.js +377 -322
- emhass/static/style.css +270 -13
- emhass/templates/configuration.html +77 -0
- emhass/templates/index.html +23 -14
- emhass/templates/template.html +4 -5
- emhass/utils.py +1797 -428
- emhass/web_server.py +850 -448
- emhass/websocket_client.py +224 -0
- emhass-0.15.5.dist-info/METADATA +164 -0
- emhass-0.15.5.dist-info/RECORD +34 -0
- {emhass-0.10.6.dist-info → emhass-0.15.5.dist-info}/WHEEL +1 -2
- emhass-0.15.5.dist-info/entry_points.txt +2 -0
- emhass-0.10.6.dist-info/METADATA +0 -622
- emhass-0.10.6.dist-info/RECORD +0 -26
- emhass-0.10.6.dist-info/entry_points.txt +0 -2
- emhass-0.10.6.dist-info/top_level.txt +0 -1
- {emhass-0.10.6.dist-info → emhass-0.15.5.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from emhass.websocket_client import AsyncWebSocketClient, ConnectionError
|
|
5
|
+
|
|
6
|
+
_global_client: AsyncWebSocketClient | None = None
|
|
7
|
+
_lock = asyncio.Lock()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def _create_and_start_client(
|
|
11
|
+
hass_url: str, token: str, logger: logging.Logger | None
|
|
12
|
+
) -> AsyncWebSocketClient:
|
|
13
|
+
"""Helper to create and start a new WebSocket client."""
|
|
14
|
+
if logger:
|
|
15
|
+
logger.debug(f"Creating new WebSocket client for {hass_url}")
|
|
16
|
+
client = AsyncWebSocketClient(hass_url=hass_url, long_lived_token=token, logger=logger)
|
|
17
|
+
try:
|
|
18
|
+
await asyncio.wait_for(client.startup(), timeout=15.0)
|
|
19
|
+
if logger:
|
|
20
|
+
logger.info("WebSocket client started successfully")
|
|
21
|
+
return client
|
|
22
|
+
except TimeoutError:
|
|
23
|
+
if logger:
|
|
24
|
+
logger.error("WebSocket startup timed out")
|
|
25
|
+
raise ConnectionError("WebSocket startup timed out") from None
|
|
26
|
+
except Exception as e:
|
|
27
|
+
if logger:
|
|
28
|
+
logger.error(f"WebSocket startup failed: {e}")
|
|
29
|
+
raise
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def _reconnect_client(client: AsyncWebSocketClient, logger: logging.Logger | None) -> None:
|
|
33
|
+
"""Helper to reconnect an existing WebSocket client."""
|
|
34
|
+
if logger:
|
|
35
|
+
logger.debug("WebSocket client exists but not connected, attempting reconnect")
|
|
36
|
+
try:
|
|
37
|
+
await asyncio.wait_for(client.reconnect(), timeout=15.0)
|
|
38
|
+
if logger:
|
|
39
|
+
logger.info("WebSocket client reconnected successfully")
|
|
40
|
+
except TimeoutError:
|
|
41
|
+
if logger:
|
|
42
|
+
logger.error("WebSocket reconnect timed out")
|
|
43
|
+
raise ConnectionError("WebSocket reconnect timed out") from None
|
|
44
|
+
except Exception as e:
|
|
45
|
+
if logger:
|
|
46
|
+
logger.error(f"WebSocket reconnect failed: {e}")
|
|
47
|
+
raise
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def get_websocket_client(
|
|
51
|
+
hass_url: str, token: str, logger: logging.Logger | None = None
|
|
52
|
+
) -> AsyncWebSocketClient:
|
|
53
|
+
"""
|
|
54
|
+
Get or create a global WebSocket client connection.
|
|
55
|
+
|
|
56
|
+
:param hass_url: The Home Assistant URL
|
|
57
|
+
:type hass_url: str
|
|
58
|
+
:param token: Long-lived token for authentication
|
|
59
|
+
:type token: str
|
|
60
|
+
:param logger: Logger instance for logging
|
|
61
|
+
:type logger: logging.Logger | None
|
|
62
|
+
:return: Connected AsyncWebSocketClient instance
|
|
63
|
+
:rtype: AsyncWebSocketClient
|
|
64
|
+
:raises ConnectionError: If connection cannot be established
|
|
65
|
+
"""
|
|
66
|
+
global _global_client
|
|
67
|
+
async with _lock:
|
|
68
|
+
if _global_client is None:
|
|
69
|
+
# Logic extracted to helper
|
|
70
|
+
_global_client = await _create_and_start_client(hass_url, token, logger)
|
|
71
|
+
elif not _global_client.connected:
|
|
72
|
+
# Logic extracted to helper
|
|
73
|
+
try:
|
|
74
|
+
await _reconnect_client(_global_client, logger)
|
|
75
|
+
except Exception:
|
|
76
|
+
# Ensure global is reset if reconnect fails
|
|
77
|
+
_global_client = None
|
|
78
|
+
raise
|
|
79
|
+
elif logger:
|
|
80
|
+
logger.debug("Using existing connected WebSocket client")
|
|
81
|
+
return _global_client
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def close_global_connection():
|
|
85
|
+
"""
|
|
86
|
+
Close the global WebSocket connection if it exists.
|
|
87
|
+
"""
|
|
88
|
+
global _global_client
|
|
89
|
+
|
|
90
|
+
async with _lock:
|
|
91
|
+
if _global_client is not None:
|
|
92
|
+
try:
|
|
93
|
+
await _global_client.shutdown()
|
|
94
|
+
except Exception:
|
|
95
|
+
# Ignore errors during shutdown
|
|
96
|
+
pass
|
|
97
|
+
finally:
|
|
98
|
+
_global_client = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def is_connected() -> bool:
|
|
102
|
+
"""
|
|
103
|
+
Check if the global WebSocket client is connected.
|
|
104
|
+
|
|
105
|
+
:return: True if connected, False otherwise
|
|
106
|
+
:rtype: bool
|
|
107
|
+
"""
|
|
108
|
+
return _global_client is not None and _global_client.connected
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
config_categorie,legacy_parameter_name,parameter,list_name
|
|
2
|
+
retrieve_hass_conf,freq,optimization_time_step
|
|
3
|
+
retrieve_hass_conf,days_to_retrieve,historic_days_to_retrieve
|
|
4
|
+
retrieve_hass_conf,var_PV,sensor_power_photovoltaics
|
|
5
|
+
retrieve_hass_conf,var_PV_forecast,sensor_power_photovoltaics_forecast
|
|
6
|
+
retrieve_hass_conf,var_load,sensor_power_load_no_var_loads
|
|
7
|
+
retrieve_hass_conf,load_negative,load_negative
|
|
8
|
+
retrieve_hass_conf,set_zero_min,set_zero_min
|
|
9
|
+
retrieve_hass_conf,var_replace_zero,sensor_replace_zero,list_sensor_replace_zero
|
|
10
|
+
retrieve_hass_conf,var_interp,sensor_linear_interp,list_sensor_linear_interp
|
|
11
|
+
retrieve_hass_conf,use_influxdb,use_influxdb
|
|
12
|
+
retrieve_hass_conf,influxdb_host,influxdb_host
|
|
13
|
+
retrieve_hass_conf,influxdb_port,influxdb_port
|
|
14
|
+
retrieve_hass_conf,influxdb_username,influxdb_username
|
|
15
|
+
retrieve_hass_conf,influxdb_password,influxdb_password
|
|
16
|
+
retrieve_hass_conf,influxdb_database,influxdb_database
|
|
17
|
+
retrieve_hass_conf,influxdb_measurement,influxdb_measurement
|
|
18
|
+
retrieve_hass_conf,influxdb_retention_policy,influxdb_retention_policy
|
|
19
|
+
retrieve_hass_conf,influxdb_use_ssl,influxdb_use_ssl
|
|
20
|
+
retrieve_hass_conf,influxdb_verify_ssl,influxdb_verify_ssl
|
|
21
|
+
retrieve_hass_conf,method_ts_round,method_ts_round
|
|
22
|
+
retrieve_hass_conf,continual_publish,continual_publish
|
|
23
|
+
retrieve_hass_conf,use_websocket,use_websocket
|
|
24
|
+
params_secrets,time_zone,time_zone
|
|
25
|
+
params_secrets,lat,Latitude
|
|
26
|
+
params_secrets,lon,Longitude
|
|
27
|
+
params_secrets,alt,Altitude
|
|
28
|
+
optim_conf,costfun,costfun
|
|
29
|
+
optim_conf,logging_level,logging_level
|
|
30
|
+
optim_conf,set_use_pv,set_use_pv
|
|
31
|
+
optim_conf,set_use_adjusted_pv,set_use_adjusted_pv
|
|
32
|
+
optim_conf,adjusted_pv_regression_model,adjusted_pv_regression_model
|
|
33
|
+
optim_conf,adjusted_pv_solar_elevation_threshold,adjusted_pv_solar_elevation_threshold
|
|
34
|
+
optim_conf,adjusted_pv_model_max_age,adjusted_pv_model_max_age
|
|
35
|
+
optim_conf,set_use_battery,set_use_battery
|
|
36
|
+
optim_conf,num_def_loads,number_of_deferrable_loads
|
|
37
|
+
optim_conf,P_deferrable_nom,nominal_power_of_deferrable_loads,list_nominal_power_of_deferrable_loads
|
|
38
|
+
optim_conf,minimum_power_of_deferrable_loads,minimum_power_of_deferrable_loads
|
|
39
|
+
optim_conf,def_total_hours,operating_hours_of_each_deferrable_load,list_operating_hours_of_each_deferrable_load
|
|
40
|
+
optim_conf,treat_def_as_semi_cont,treat_deferrable_load_as_semi_cont,list_treat_deferrable_load_as_semi_cont
|
|
41
|
+
optim_conf,set_def_constant,set_deferrable_load_single_constant,list_set_deferrable_load_single_constant
|
|
42
|
+
optim_conf,def_start_penalty,set_deferrable_startup_penalty,list_set_deferrable_startup_penalty
|
|
43
|
+
optim_conf,delta_forecast,delta_forecast_daily
|
|
44
|
+
optim_conf,load_forecast_method,load_forecast_method
|
|
45
|
+
optim_conf,load_cost_forecast_method,load_cost_forecast_method
|
|
46
|
+
optim_conf,load_cost_hp,load_peak_hours_cost
|
|
47
|
+
optim_conf,load_cost_hc,load_offpeak_hours_cost
|
|
48
|
+
optim_conf,prod_price_forecast_method,production_price_forecast_method
|
|
49
|
+
optim_conf,prod_sell_price,photovoltaic_production_sell_price
|
|
50
|
+
optim_conf,set_total_pv_sell,set_total_pv_sell
|
|
51
|
+
optim_conf,lp_solver,lp_solver
|
|
52
|
+
optim_conf,lp_solver_path,lp_solver_path
|
|
53
|
+
optim_conf,lp_solver_timeout,lp_solver_timeout
|
|
54
|
+
optim_conf,num_threads,num_threads
|
|
55
|
+
optim_conf,set_nocharge_from_grid,set_nocharge_from_grid
|
|
56
|
+
optim_conf,set_nodischarge_to_grid,set_nodischarge_to_grid
|
|
57
|
+
optim_conf,set_battery_dynamic,set_battery_dynamic
|
|
58
|
+
optim_conf,battery_dynamic_max,battery_dynamic_max
|
|
59
|
+
optim_conf,battery_dynamic_min,battery_dynamic_min
|
|
60
|
+
optim_conf,weight_battery_discharge,weight_battery_discharge
|
|
61
|
+
optim_conf,weight_battery_charge,weight_battery_charge
|
|
62
|
+
optim_conf,weather_forecast_method,weather_forecast_method
|
|
63
|
+
optim_conf,open_meteo_cache_max_age,open_meteo_cache_max_age
|
|
64
|
+
optim_conf,def_start_timestep,start_timesteps_of_each_deferrable_load,list_start_timesteps_of_each_deferrable_load
|
|
65
|
+
optim_conf,def_end_timestep,end_timesteps_of_each_deferrable_load,list_end_timesteps_of_each_deferrable_load
|
|
66
|
+
optim_conf,list_hp_periods,load_peak_hour_periods
|
|
67
|
+
optim_conf,model_type,model_type
|
|
68
|
+
optim_conf,var_model,var_model
|
|
69
|
+
optim_conf,sklearn_model,sklearn_model
|
|
70
|
+
optim_conf,num_lags,num_lags
|
|
71
|
+
optim_conf,split_date_delta,split_date_delta
|
|
72
|
+
optim_conf,perform_backtest,perform_backtest
|
|
73
|
+
plant_conf,P_from_grid_max,maximum_power_from_grid
|
|
74
|
+
plant_conf,P_to_grid_max,maximum_power_to_grid
|
|
75
|
+
plant_conf,module_model,pv_module_model,list_pv_module_model
|
|
76
|
+
plant_conf,inverter_model,pv_inverter_model,list_pv_inverter_model
|
|
77
|
+
plant_conf,surface_tilt,surface_tilt,list_surface_tilt
|
|
78
|
+
plant_conf,surface_azimuth,surface_azimuth,list_surface_azimuth
|
|
79
|
+
plant_conf,modules_per_string,modules_per_string,list_modules_per_string
|
|
80
|
+
plant_conf,strings_per_inverter,strings_per_inverter,list_strings_per_inverter
|
|
81
|
+
plant_conf,inverter_is_hybrid,inverter_is_hybrid
|
|
82
|
+
plant_conf,inverter_ac_output_max,inverter_ac_output_max
|
|
83
|
+
plant_conf,inverter_ac_input_max,inverter_ac_input_max
|
|
84
|
+
plant_conf,inverter_efficiency_dc_ac,inverter_efficiency_dc_ac
|
|
85
|
+
plant_conf,inverter_efficiency_ac_dc,inverter_efficiency_ac_dc
|
|
86
|
+
plant_conf,inverter_stress_cost,inverter_stress_cost
|
|
87
|
+
plant_conf,inverter_stress_segments,inverter_stress_segments
|
|
88
|
+
plant_conf,compute_curtailment,compute_curtailment
|
|
89
|
+
plant_conf,Pd_max,battery_discharge_power_max
|
|
90
|
+
plant_conf,Pc_max,battery_charge_power_max
|
|
91
|
+
plant_conf,eta_disch,battery_discharge_efficiency
|
|
92
|
+
plant_conf,eta_ch,battery_charge_efficiency
|
|
93
|
+
plant_conf,Enom,battery_nominal_energy_capacity
|
|
94
|
+
plant_conf,SOCmin,battery_minimum_state_of_charge
|
|
95
|
+
plant_conf,SOCmax,battery_maximum_state_of_charge
|
|
96
|
+
plant_conf,SOCtarget,battery_target_state_of_charge
|
|
97
|
+
plant_conf,battery_stress_cost,battery_stress_cost
|
|
98
|
+
plant_conf,battery_stress_segments,battery_stress_segments
|
emhass/data/cec_inverters.pbz2
CHANGED
|
Binary file
|
emhass/data/cec_modules.pbz2
CHANGED
|
Binary file
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"logging_level": "INFO",
|
|
3
|
+
"costfun": "profit",
|
|
4
|
+
"optimization_time_step": 30,
|
|
5
|
+
"historic_days_to_retrieve": 9,
|
|
6
|
+
"method_ts_round": "nearest",
|
|
7
|
+
"continual_publish": false,
|
|
8
|
+
"data_path": "default",
|
|
9
|
+
"set_total_pv_sell": false,
|
|
10
|
+
"lp_solver": "default",
|
|
11
|
+
"lp_solver_path": "empty",
|
|
12
|
+
"lp_solver_timeout": 45,
|
|
13
|
+
"num_threads": 0,
|
|
14
|
+
"set_nocharge_from_grid": false,
|
|
15
|
+
"set_nodischarge_to_grid": true,
|
|
16
|
+
"set_battery_dynamic": false,
|
|
17
|
+
"battery_dynamic_max": 0.9,
|
|
18
|
+
"battery_dynamic_min": -0.9,
|
|
19
|
+
"weight_battery_discharge": 0.0,
|
|
20
|
+
"weight_battery_charge": 0.0,
|
|
21
|
+
"sensor_power_photovoltaics": "sensor.power_photovoltaics",
|
|
22
|
+
"sensor_power_photovoltaics_forecast": "sensor.p_pv_forecast",
|
|
23
|
+
"sensor_power_load_no_var_loads": "sensor.power_load_no_var_loads",
|
|
24
|
+
"sensor_replace_zero": ["sensor.power_photovoltaics", "sensor.p_pv_forecast"],
|
|
25
|
+
"sensor_linear_interp": [
|
|
26
|
+
"sensor.power_photovoltaics",
|
|
27
|
+
"sensor.power_load_no_var_loads"
|
|
28
|
+
],
|
|
29
|
+
"use_websocket": false,
|
|
30
|
+
"use_influxdb": false,
|
|
31
|
+
"influxdb_host": "localhost",
|
|
32
|
+
"influxdb_port": 8086,
|
|
33
|
+
"influxdb_username": "",
|
|
34
|
+
"influxdb_password": "",
|
|
35
|
+
"influxdb_database": "homeassistant",
|
|
36
|
+
"influxdb_measurement": "W",
|
|
37
|
+
"influxdb_retention_policy": "autogen",
|
|
38
|
+
"influxdb_use_ssl": false,
|
|
39
|
+
"influxdb_verify_ssl": false,
|
|
40
|
+
"load_negative": false,
|
|
41
|
+
"set_zero_min": true,
|
|
42
|
+
"number_of_deferrable_loads": 2,
|
|
43
|
+
"nominal_power_of_deferrable_loads": [3000.0, 750.0],
|
|
44
|
+
"minimum_power_of_deferrable_loads": [0.0, 0.0],
|
|
45
|
+
"operating_hours_of_each_deferrable_load": [4, 0],
|
|
46
|
+
"weather_forecast_method": "open-meteo",
|
|
47
|
+
"open_meteo_cache_max_age": 30,
|
|
48
|
+
"load_forecast_method": "naive",
|
|
49
|
+
"model_type": "load_forecast",
|
|
50
|
+
"var_model": "sensor.power_load_no_var_loads",
|
|
51
|
+
"sklearn_model": "KNeighborsRegressor",
|
|
52
|
+
"num_lags": 48,
|
|
53
|
+
"split_date_delta": "48h",
|
|
54
|
+
"perform_backtest": false,
|
|
55
|
+
"delta_forecast_daily": 1,
|
|
56
|
+
"load_cost_forecast_method": "hp_hc_periods",
|
|
57
|
+
"start_timesteps_of_each_deferrable_load": [0, 0],
|
|
58
|
+
"end_timesteps_of_each_deferrable_load": [0, 0],
|
|
59
|
+
"load_peak_hour_periods": {
|
|
60
|
+
"period_hp_1": [
|
|
61
|
+
{
|
|
62
|
+
"start": "02:54"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"end": "15:24"
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
"period_hp_2": [
|
|
69
|
+
{
|
|
70
|
+
"start": "17:24"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"end": "20:24"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"treat_deferrable_load_as_semi_cont": [true, true],
|
|
78
|
+
"set_deferrable_load_single_constant": [false, false],
|
|
79
|
+
"set_deferrable_startup_penalty": [0.0, 0.0],
|
|
80
|
+
"load_peak_hours_cost": 0.1907,
|
|
81
|
+
"load_offpeak_hours_cost": 0.1419,
|
|
82
|
+
"production_price_forecast_method": "constant",
|
|
83
|
+
"photovoltaic_production_sell_price": 0.1419,
|
|
84
|
+
"maximum_power_from_grid": 9000,
|
|
85
|
+
"maximum_power_to_grid": 9000,
|
|
86
|
+
"set_use_pv": false,
|
|
87
|
+
"set_use_adjusted_pv": false,
|
|
88
|
+
"adjusted_pv_regression_model": "LassoRegression",
|
|
89
|
+
"adjusted_pv_solar_elevation_threshold": 10,
|
|
90
|
+
"adjusted_pv_model_max_age": 24,
|
|
91
|
+
"pv_module_model": [
|
|
92
|
+
"CSUN_Eurasia_Energy_Systems_Industry_and_Trade_CSUN295_60M"
|
|
93
|
+
],
|
|
94
|
+
"pv_inverter_model": [
|
|
95
|
+
"Fronius_International_GmbH__Fronius_Primo_5_0_1_208_240__240V_"
|
|
96
|
+
],
|
|
97
|
+
"surface_tilt": [30],
|
|
98
|
+
"surface_azimuth": [205],
|
|
99
|
+
"modules_per_string": [16],
|
|
100
|
+
"strings_per_inverter": [1],
|
|
101
|
+
"inverter_is_hybrid": false,
|
|
102
|
+
"inverter_ac_output_max": 5000,
|
|
103
|
+
"inverter_ac_input_max": 5000,
|
|
104
|
+
"inverter_efficiency_dc_ac": 1.0,
|
|
105
|
+
"inverter_efficiency_ac_dc": 1.0,
|
|
106
|
+
"inverter_stress_cost": 0.0,
|
|
107
|
+
"inverter_stress_segments": 10,
|
|
108
|
+
"compute_curtailment": false,
|
|
109
|
+
"set_use_battery": false,
|
|
110
|
+
"battery_discharge_power_max": 1000,
|
|
111
|
+
"battery_charge_power_max": 1000,
|
|
112
|
+
"battery_discharge_efficiency": 0.95,
|
|
113
|
+
"battery_charge_efficiency": 0.95,
|
|
114
|
+
"battery_nominal_energy_capacity": 5000,
|
|
115
|
+
"battery_minimum_state_of_charge": 0.3,
|
|
116
|
+
"battery_maximum_state_of_charge": 0.9,
|
|
117
|
+
"battery_target_state_of_charge": 0.6,
|
|
118
|
+
"battery_stress_cost": 0.0,
|
|
119
|
+
"battery_stress_segments": 10
|
|
120
|
+
}
|