Publish data to Home Assistant#

The publish-data specificities#

publish-data (which is either run manually or automatically via continual_publish or Home Assistant automation), will push the optimization results to Home Assistant for each deferrable load defined in the configuration. For example, if you have defined two deferrable loads, then the command will publish sensor.p_deferrable0 and sensor.p_deferrable1 to Home Assistant. When the dayahead-optim is launched, after the optimization, either entity json files or a csv file will be saved on disk. The publish-data command will load the latest csv/json files and select one saved row by comparing the saved timestamps with the current time (taken in the configured time zone and truncated to the minute). The saved timestamps are the optimization grid of the run that produced the file, so with a 30-minute time step they are normally 00:00, 00:30, 01:00, 01:30, … and so on. The row is chosen by method_ts_round:

  • nearest (default): the saved timestamp closest to the current time. If the current time is 00:05, the row published is 00:00; if it is 00:25, it is 00:30. An exact midpoint (for example 00:15 with 30-minute rows) selects the later row.

  • first: the latest saved timestamp that is not after the current time (00:05 and 00:25 both publish the 00:00 row).

  • last: the earliest saved timestamp that is not before the current time (00:05 and 00:25 both publish the 00:30 row).

If no saved timestamp qualifies for first or last (the plan is entirely in the future or entirely in the past), the closest one is published. The saved timestamps carry their UTC offset, so on a daylight saving day both occurrences of a repeated local hour are distinct rows and each is published at its own real time.

The publish-data command will also publish PV and load forecast data on sensors p_pv_forecast and p_load_forecast. If using a battery, then the battery-optimized power and the SOC will be published on sensors p_batt_forecast and soc_batt_forecast. On these sensors, the future values are passed as nested attributes.

Compact Home Assistant entities (publish_horizon_attributes)#

By default, the published forecast and schedule entities that expose future values (PV/load forecasts, deferrable-load power and command schedules, battery scheduled power/SOC, predicted temperatures, unit load-cost / production-price forecasts) carry the optimization horizon as a list/schedule state attribute (forecasts, deferrables_schedule, battery_scheduled_power, battery_scheduled_soc, predicted_temperatures, unit_load_cost_forecasts, schedule, …). Scalar-only entities such as sensor.optim_status and the cost totals never carried such an attribute and are unaffected. With a long MPC horizon (e.g. 24 h at a 5-minute step) these attribute maps can exceed Home Assistant’s 16 KiB Recorder attribute-size limit, which makes the Recorder drop the entire attribute map for that state - including unit_of_measurement, device_class and state_class.

Set publish_horizon_attributes to false (it defaults to true) to keep those Home Assistant entities compact:

  • true (default / unset): current behaviour - the horizon is attached as a list/schedule attribute to the entities that expose future values.

  • false: for those same entities EMHASS publishes only the current scalar state plus the metadata they already have (friendly_name, and where present device_class, unit_of_measurement, state_class). The full-horizon list/schedule attribute is not attached. No new metadata is added to entities that do not already have it (for example the categorical sensor.p_deferrableX_state keeps just its friendly_name).

This is a publication-layer option only. It does not change the optimization, the optimization horizon, the result DataFrames/CSV, the saved full-series entity files, or custom entity-id/prefix behaviour. It is resolved from the normal EMHASS configuration and runtime-parameter precedence (like continual_publish) and applies to direct publish-data, publication after an optimization, and saved-entity publication. The continual_publish background loop uses the value resolved when the loop first starts, so set publish_horizon_attributes in the static configuration for that loop rather than expecting a later per-action runtime override to retune an already-running loop.

The complete plan is always available regardless of this setting:

  • /api/v1/plan remains the supported full-plan interface (the published scalar state matches the plan’s current/first row).

  • /api/v1/last-run can be used to correlate a plan with the run that produced it and to check its freshness.

If number_of_batteries is greater than 1 (see Configuration), sensor.p_batt_forecast stays the fleet total (unchanged), but there is no meaningful fleet-wide SOC, so sensor.soc_batt_forecast is not published. Instead, EMHASS publishes one power sensor and one SOC sensor per battery, on fixed entity ids: sensor.p_batt_forecast_battery0, sensor.p_batt_forecast_battery1, …, and sensor.soc_batt_forecast_battery0, sensor.soc_batt_forecast_battery1, … The friendly names get a Battery K suffix (e.g. Battery Power Forecast Battery 0). With number_of_batteries at its default of 1, this is a true no-op: exactly today’s two entities, nothing new. These per-battery entity ids are fixed and not customizable at runtime (there is no per-battery equivalent of custom_batt_forecast_id - see the note below), which keeps a multi-battery config from adding new runtime surface.

If you run publish manually (or via a Home Assistant Automation), it is possible to provide custom sensor names for all the data exported by the publish-data command. For this, when using the publish-data endpoint we can just add some runtime parameters as dictionaries like this:

shell_command:
  publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{\"custom_load_forecast_id\": {\"entity_id\": \"sensor.p_load_forecast\", \"unit_of_measurement\": \"W\", \"friendly_name\": \"Load Power Forecast\"}}' http://localhost:5000/action/publish-data"

These keys are available to modify: custom_pv_forecast_id, custom_load_forecast_id, custom_batt_forecast_id, custom_batt_soc_forecast_id, custom_grid_forecast_id, custom_cost_fun_id, custom_deferrable_forecast_id, custom_unit_load_cost_id and custom_unit_prod_price_id.

With number_of_batteries greater than 1, custom_batt_forecast_id keeps overriding only the fleet-total power entity (the same one it overrides today); the per-battery power entities always stay on their fixed sensor.p_batt_forecast_batteryX ids. custom_batt_soc_forecast_id has no effect at all when number_of_batteries is greater than 1 - there is no single bare SOC entity left to override, since SOC has no meaningful fleet aggregate - so it is ignored with a logged warning rather than being silently reinterpreted as a prefix for the per-battery SOC entities. This was chosen as the least-surprising option: it keeps every per-battery entity id exactly the fixed name documented above, and does not add a new “per-battery custom id list” runtime parameter.

If you provide the custom_deferrable_forecast_id then the passed data should be a list of dictionaries, like this:

shell_command:
  publish_data: "curl -i -H \"Content-Type:application/json\" -X POST -d '{\"custom_deferrable_forecast_id\": [{\"entity_id\": \"sensor.p_deferrable0\",\"unit_of_measurement\": \"W\", \"friendly_name\": \"Deferrable Load 0\"},{\"entity_id\": \"sensor.p_deferrable1\",\"unit_of_measurement\": \"W\", \"friendly_name\": \"Deferrable Load 1\"}]}' http://localhost:5000/action/publish-data"

You should be careful that the list of dictionaries has the correct length, which is the number of defined deferrable loads.

Computed variables and published data#

Below you can find a list of the variables resulting from EMHASS computation, shown in the charts and published to Home Assistant through the publish_data command:

EMHASS variable

Definition

Home Assistant published sensor

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

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

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 optimize energy usage by prioritizing 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

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 advantageous to consume from the grid.

-

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.

-

P_batt

Forecasted (dis)charge power load (Watts) for the battery (if installed), or the fleet total if number_of_batteries is greater than 1. If negative it indicates the battery is charging, if positive that the battery is discharging.

sensor.p_batt_forecast

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 import, while a negative value indicates net export.

sensor.p_grid_forecast

SOC_opt

Forecasted battery optimized Status Of Charge (SOC) percentage level. Only published when number_of_batteries is 1 (there is no meaningful fleet-wide SOC for a multi-battery plant).

sensor.soc_batt_forecast

P_batt_X / SOC_opt_X
[X = 0, 1, 2, …]

Only published when number_of_batteries is greater than 1: the (dis)charge power and SOC of battery X individually.

sensor.p_batt_forecast_batteryX / sensor.soc_batt_forecast_batteryX

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

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

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

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

optim_status

This contains the status of the latest execution and is the same you can see in the Log following an optimization job. Optimal: the plan is the solver’s optimum. Optimal_Inaccurate: a feasible plan the solver could not prove optimal (for example a CPLEX time limit; the default HiGHS solver does not return this status, and a Gurobi time limit with a feasible plan is reported as Optimal (Incumbent)); the plan is published. Optimal (Incumbent): the solve hit lp_solver_timeout after finding a feasible plan, and that plan is used; it satisfies every constraint but may not be optimal. Optimal (Relaxed): the full problem could not be solved, or timed out before any feasible plan was found, so EMHASS solved it again without the semi-continuous and single-constant on/off constraints (mutual-exclusion groups are kept); the plan is published but may, for example, run a semi-continuous load below its nominal power. Infeasible or another status: no usable plan. /api/v1/last-run reports every status with a published plan (Optimal, Optimal_Inaccurate, Optimal (Incumbent), Optimal (Relaxed)) as ok, and /api/v1/plan serves the latest of those plans; a failed run does not replace it.

sensor.optim_status

Deferrable load command states (optional)#

By default each deferrable load is published as a power sensor (sensor.p_deferrableX), and your automations decide what to do with that power value (e.g. by thresholding it). If you would rather act on a clear, ready-made command, enable the publish_deferrable_load_states option.

When enabled, EMHASS additionally publishes one command sensor per deferrable load:

Variable

Description

Published Sensor

P_deferrableX_state
[X = 0, 1, 2, …]

An interpretable command for deferrable load X at the current timestep: off (idle), on (running at nominal power) or variable (a modulated level in between). The full optimized plan over the horizon is attached as a schedule attribute (a list of {date, value} entries). This is a generic, automation-agnostic command surface — you map on/off/variable onto your own switch, number or script as you see fit.

sensor.p_deferrableX_state

The labels are derived from the optimized P_deferrableX power and the load’s nominal_power_of_deferrable_loads, so they stay consistent with the power forecast. The default is false, so the zero-config behaviour is unchanged.

The entity ids can be overridden at runtime with custom_deferrable_state_id (a list of dictionaries, one per deferrable load, mirroring custom_deferrable_forecast_id).

Alternative publish methods#

Due to the flexibility of EMHASS, multiple different approaches to publishing the optimization results have been created. Select an option that best meets your use case:

Publish last optimization (manual)#

By default, running an optimization in EMHASS will output the results into the CSV file: data_path/opt_res_latest.csv (overriding the existing data on that file). We run the publish command to publish the last optimization saved in the opt_res_latest.csv:

# RUN dayahead
curl -i -H 'Content-Type:application/json' -X POST -d {} http://localhost:5000/action/dayahead-optim
# Then publish the results of dayahead
curl -i -H 'Content-Type:application/json' -X POST -d {} http://localhost:5000/action/publish-data

Note, the published entities from the publish-data action will not automatically update the entities’ current state (current state being used to check when to turn on and off appliances via Home Assistant automations). To update the EMHASS entities state, another publish would have to be re-run later when the current time matches the next value’s timestamp (e.g. every 30 minutes). See examples below for methods to automate the publish-action.

Note

If your are using the thermal model, then for this manual publish implementation you need to indicate which of your deferrable loads are thermal. We do this with the "def_load_config" entry.

If you have just one deferrable load which is a thermal load then the payload of curl publish command should look like: {"def_load_config": [{"thermal_config": {}}]}

Similarly if you have three deferrable loads where the second load is a thermal load the payload would have been: {"def_load_config": [{},{"thermal_config": {}},{}]}

Continual_publish (EMHASS Automation)#

Setting continual_publish to true in the configuration saves the output of the optimization into the data_path/entities folder (a .json file for each sensor/entity). A constant loop (in optimization_time_step minutes) will run, observe the .json files in that folder, and publish the saved files periodically (updating the current state of the entity by comparing date.now with the saved data value timestamps).

For users that wish to run multiple different optimizations, you can set the runtime parameter: publish_prefix to something like: "mpc_" or "dh_". This will generate unique entity_id names per optimization and save these unique entities as separate files in the folder. All the entity files will then be updated when the next loop iteration runs. If a different optimization_time_step integer was passed as a runtime parameter in an optimization, the continual_publish loop will be based on the lowest optimization_time_step saved. An example:

# RUN dayahead, with optimization_time_step=30 (default), prefix=dh_ 
curl -i -H 'Content-Type:application/json' -X POST -d '{"publish_prefix":"dh_"}' http://localhost:5000/action/dayahead-optim
# RUN MPC, with optimization_time_step=5, prefix=mpc_
curl -i -H 'Content-Type:application/json' -X POST -d '{'optimization_time_step':5,"publish_prefix":"mpc_"}' http://localhost:5000/action/naive-mpc-optim

This will tell continual_publish to loop every 5 minutes based on the optimization_time_step passed in MPC. All entities from the output of dayahead “dh_” and MPC “mpc_” will be published every 5 minutes.


Note

It is recommended to use the 2 other options below once you have a more advanced understanding of EMHASS and/or Home Assistant.

Mixture of continual_publish and manual (Home Assistant Automation for Publish)#

You can choose to save one optimization for continual_publish and bypass another optimization by setting 'continual_publish':false runtime parameter:

# RUN dayahead, with optimization_time_step=30 (default), prefix=dh_, included into continual_publish
curl -i -H 'Content-Type:application/json' -X POST -d '{"publish_prefix":"dh_"}' http://localhost:5000/action/dayahead-optim

# RUN MPC, with optimization_time_step=5, prefix=mpc_, Manually publish, excluded from continual_publish loop
curl -i -H 'Content-Type:application/json' -X POST -d '{'continual_publish':false,'optimization_time_step':5,"publish_prefix":"mpc_"}' http://localhost:5000/action/naive-mpc-optim
# Publish MPC output
curl -i -H 'Content-Type:application/json' -X POST -d {} http://localhost:5000/action/publish-data

This example saves the dayahead optimization into data_path/entities as .json files, being included in the continutal_publish loop (publishing every 30 minutes). The MPC optimization will not be saved in data_path/entities, and therefore only into data_path/opt_res_latest.csv. Requiring a publish-data action to be run manually (or via a Home Assistant) Automation for the MPC results.

Manual (Home Assistant Automation for Publish)#

For users who wish to have full control of exactly when they would like to run a publish and have the ability to save multiple different optimizations. The entity_save runtime parameter has been created to save the optimization output entities to .json files whilst continual_publish is set to false in the configuration. Allowing the user to reference the saved .json files manually via a publish:

in configuration page/config.json :

"continual_publish": false

POST action :

# RUN dayahead, with optimization_time_step=30 (default), prefix=dh_, save entity
curl -i -H 'Content-Type:application/json' -X POST -d '{"entity_save": true, "publish_prefix":"dh_"}' http://localhost:5000/action/dayahead-optim
# RUN MPC, with optimization_time_step=5, prefix=mpc_, save entity
curl -i -H 'Content-Type:application/json' -X POST -d '{"entity_save": true", 'optimization_time_step':5,"publish_prefix":"mpc_"}' http://localhost:5000/action/naive-mpc-optim

You can then reference these .json saved entities via their publish_prefix. Include the same publish_prefix in the publish_data action:

#Publish the MPC optimization ran above 
curl -i -H 'Content-Type:application/json' -X POST -d '{"publish_prefix":"mpc_"}'  http://localhost:5000/action/publish-data

This will publish all entities from the MPC (_mpc) optimization above.
Alternatively, you can choose to publish all the saved files .json files with publish_prefix = all:

#Publish all saved entities
curl -i -H 'Content-Type:application/json' -X POST -d '{"publish_prefix":"all"}'  http://localhost:5000/action/publish-data

This action will publish the dayahead (_dh) and MPC (_mpc) optimization results from the optimizations above.