% SPDX-License-Identifier: Apache-2.0 (resource-page)= # Resources (Device Simulations) ## Concepts The simulations for resources are leaning on general concepts of the [S2 standard]. ### Control Types The control of resources and such what a resource simulation will simulate follows three basic control principles: - Operation Mode Based Control (OMBC) - Fill Rate Based Control (FRBC) - Demand Driven Based Control (DDBC) Although these control principles differ enough to separate them into three distinct control types, there are some common aspects that make them similar: - Operation Modes - Transitions and - Timers. The objective for a control type is under which circumstances what things can be adjusted, and what the constraints are for these adjustments. The three control types model a virtual, abstract resource for simulation. The abstract resource ignores all details of pyhsical device that are not relevant to energy management. In addition, physical devices have an enormous variety in parameters, sensors, control strategies, concerns, safeguards, and so on. It would be practically impossible to develop a simulation that can understand all the parameters of all the physical devices on the market. By making the resource more abstract, its concepts can be translated to all sorts of physical devices, even though internally they function very differently. As a consequence, it not always possible to make a 100% accurate description of all the behaviors and constraints in these abstractions. But the abstractions used in the control types are quite powerful, and should allow you to come pretty close. The control types basically define how the simulated resource can be described. The user in the end selects the proper desciption of a physical device using the configuration options provided for resource simulations. The configuration sets how the simulated resource functions, what it can do and what kind of constraints it has. ### Resource Simulation Based on the description of this virtual resource, the resource simulation can make predictions of what the physical device will do in certain situations, and when it is allowed to execute instructions generated by the optimization as part of the energy management plan evaluation. ### Resource Status Once the physical device has changed it's behavior, the resource simulation should be informed to make the simulation change it's state accordingly. The actual state of a pyhsical device may be reported to the resource simulation by the **PUT** `/v1/resource/status` API endpoint. ## Battery There is a wealth of possible battery operation modes: | Mode | Purpose / Behavior | Typical Trigger / Context | | ------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | | **IDLE** | Battery neither charges nor discharges (SOC stable). | No active control objective or power imbalance below thresholds. | | **SELF_CONSUMPTION** | Charge from PV surplus and discharge to cover local load. | PV generation > load (charge) or load > PV (discharge). | | **NON_EXPORT** | Charge from on-site or local surplus with the goal of minimizing or preventing energy export to the external grid. Discharging to the grid is not allowed. | Export limit reached and SOC < SOC_max. | | **PEAK_SHAVING** | Discharge to keep grid import below a target threshold. | Predicted or measured site load exceeds peak limit. | | **GRID_SUPPORT_EXPORT** | Discharge energy to grid for revenue (V2G, wholesale market, flexibility service). | Market or signal permits profitable export. | | **GRID_SUPPORT_IMPORT** | Charge from grid to absorb surplus or provide up-regulation service. | Low-price or grid-support signal detected. | | **FREQUENCY_REGULATION** | Rapid charge/discharge response to grid frequency deviations. | Active participation in frequency control. | | **RAMP_RATE_CONTROL** | Smooth site-level power ramp rates by buffering fluctuations. | Sudden PV/load change exceeding ramp limit. | | **RESERVE_BACKUP** | Maintain SOC ≥ reserve threshold to ensure backup capacity. | Resilience mode active, grid operational. | | **OUTAGE_SUPPLY** | Islanded operation: power local loads using stored energy (and PV if available). | Grid failure detected. | | **FORCED_CHARGE** | Manual or external control command to charge (e.g., pre-event, maintenance). No discharge. | Operator or optimizer command. | | **FORCED_DISCHARGE** | Manual or external control command to discharge. No charge. | Operator or optimizer command. | | **FAULT** | Battery unavailable due to fault, safety, or protection state. | Fault detected (thermal, voltage, comms, etc.). | The optimization algorithm, the device simulation and the configuration properties only support the most important of these modes. ### Battery Simulation The battery simulation assumes an idealized battery model. Under this model, the battery can be operated in three discrete operation modes with fill rate based control (FRBC): | **Operation Mode ID** | **Description** | | ------------------------ | --------------------------------------------------------------------- | | **SELF_CONSUMPTION** | Charge from local surplus and discharge to cover local load. | | **NON_EXPORT** | Charge from local surplus and do not discharge. | | **FORCED_CHARGE** | Charge. | The **operation mode factor** (0.0–1.0) specifies the normalized power rate relative to the battery's nominal maximum charge or discharge power. A value of 1.0 corresponds to full-rate charging or discharging, while 0.0 indicates no power transfer. Intermediate values scale the power proportionally. The **fill level** (0.0–1.0) specifies the normalized fill level relative to the battery's nominal maximum charge. A value of 1.0 corresponds to full while 0.0 indicates empty. Intermediate values scale the fill level proportionally. ### Battery Configuration The technical data of a battery or electric vehicle comes from a **battery profile**. The configuration of the device (`devices/batteries/` or `devices/electric_vehicles/`) selects the profile, adjusts it to the installed unit and sets how the battery is operated: | **Field** | **Description** | | -------------------------------------------------- | --------------------------------------------------------------- | | `battery_profile` | Battery profile name (without `.json`), default `generic-lfp`. | | `profile_adjustment/nominal_capacity_wh` | Capacity of the installed unit [Wh]. Replaces the profile's. | | `profile_adjustment/max_charge_power_w` | Charge power limit of the installation [W]. Only lowers. | | `profile_adjustment/max_discharge_power_w` | Discharge power limit of the installation [W]. Only lowers. | | `profile_adjustment/full_charge_interval_days` | Days between two full charges [d]. Replaces the profile's. | | `profile_adjustment/full_charge_detect_percentage` | SoC that counts as full [%]. Replaces the profile's. | | `min_soc_percentage` | Minimum SoC [%], at least the technical minimum of the profile. | | `max_soc_percentage` | Maximum SoC [%], at most the technical maximum of the profile. | ```json "house": { "battery_profile": "generic-lfp", "profile_adjustment": { "nominal_capacity_wh": 10240, "max_charge_power_w": 5000, "max_discharge_power_w": 5000 }, "min_soc_percentage": 10, "max_soc_percentage": 95 } ``` EOS resolves every effective parameter from exactly two sources, the profile and the profile adjustment, evaluated at the operating point (battery temperature and SoC). There are no built-in fallback values: - **Capacity**: `profile_adjustment/nominal_capacity_wh`, else the nominal capacity of the profile. A generic profile such as `generic-lfp` describes its limits as C-rates and has no capacity of its own, so the adjustment has to provide it. - **Power limits**: the profile limit maps give the maximum charge and discharge rate per temperature and SoC. The power limits of the adjustment can only lower them. At temperatures outside the hard cutoffs of the profile charging or discharging is inhibited. - **Efficiencies**: the profile efficiency curves at the charge and discharge power. The GENETIC optimization evaluates the profile at the measured battery temperature, or at 25 °C if no temperature is measured. Its battery simulation then limits the charge and discharge power by the SoC at the start of each charge or discharge step, and takes the charge and discharge efficiency at the power of the slot. The resolved values are shown as read-only fields of the device configuration (`effective_nominal_capacity_wh`, `effective_max_charge_power_w`, `effective_max_discharge_power_w`, `effective_charging_efficiency`, `effective_discharging_efficiency`, `effective_min_soc_percentage`, `effective_max_soc_percentage`, `effective_full_charge_interval_days`, `effective_full_charge_detect_percentage`). These fields are evaluated at the reference operating point of 25 °C and 50 % SoC. If a battery cannot be resolved, e.g. its profile does not exist or a generic profile lacks the capacity in the adjustment, its read-only fields are empty (`null`) and EOS logs an error naming the battery whenever the configuration is set up or changed. A power limit in the adjustment above the profile limit has no effect and is logged as a warning. The parameter preparation of the GENETIC and GENETIC0 optimizations always succeeds for such a configuration error. It resolves whatever the configuration allows and fills only the rest with the defaults of earlier versions, and it logs an error that names the battery, the problem and the default values used: - **Profile not usable** (missing, unreadable, invalid): the packaged generic profile `generic-lfp` with the profile adjustment. - **Generic profile not usable either**: the profile adjustment where set, else 8000 Wh, 5000 W charge and discharge power and efficiencies of 0.88, without SoC dependent power limits. - **Generic profile without capacity in the adjustment**: the profile with a capacity of 8000 Wh. - **SoC range outside the technical SoC bounds of the profile**: the technical SoC range. A profile that inhibits charging at the measured battery temperature is not a configuration error; the optimization reports it as error. :::{admonition} Note :class: note Configurations of earlier versions are migrated automatically. `capacity_wh` becomes `profile_adjustment/nominal_capacity_wh` and `max_charge_power_w` becomes the installation limit `profile_adjustment/max_charge_power_w`. A battery that relied on the former defaults gets them written into its profile adjustment (8000 Wh, 5000 W), so its capacity and charge limit do not change. The former fields `charging_efficiency` and `discharging_efficiency` are computed from the profile now and are dropped. The periodic full charge settings `full_charge_interval_days` and `full_charge_detect_percentage` move into the profile adjustment, `cell_chemistry` is dropped (the profile has the `chemistry`). Home Assistant `config_entity_ids` that point at moved fields are rewritten to the new paths. An entity on the former `max_charge_power_w` sets both `profile_adjustment/max_charge_power_w` and `profile_adjustment/max_discharge_power_w`, unless the discharge limit has its own entity. ::: #### Battery Profiles A battery profile is a JSON document with: - charge and discharge limit maps over temperature and SoC (as C-rate, W or A), with their axes and units, - charge and discharge efficiency curves over the C-rate, - technical SoC bounds and hard temperature cutoffs for charging and discharging, - the nominal capacity, except for a generic profile, - optionally the nominal voltage and model power limits, - optionally the cell chemistry and the periodic full charge the battery management system needs to recalibrate its SoC: `full_charge_interval_days` (none by default) and `full_charge_detect_percentage` (default 99), see the periodic full charge of the GENETIC optimization. All of these are required, except the optional ones. Unknown keys are rejected, so a misspelled key fails validation instead of being ignored. EOS ships the profile `generic-lfp` for a generic LFP home battery. Profiles are looked up by name first in the user data directory `/profile/battery/` and then in the packaged profiles. Saving a profile always writes to the user data directory; a user profile with the name of a packaged profile replaces it until the user profile is deleted. Changed profiles are used on their next access, without a restart of EOS. A profile name is the file name without `.json`: letters, digits, `.`, `_` and `-`, starting with a letter or digit, at most 100 characters. `schema` is reserved. Profiles can be managed with the REST API: | **Endpoint** | **Description** | | -------------------------------------- | ---------------------------------------------------------------- | | **GET** `/v1/profile` | List the profile kinds (e.g. `battery`). | | **GET** `/v1/profile/{kind}` | List the user and packaged profiles of a kind. | | **GET** `/v1/profile/{kind}/schema` | JSON schema of the profile documents of a kind. | | **GET** `/v1/profile/{kind}/{name}` | Get a profile document. | | **PUT** `/v1/profile/{kind}/{name}` | Validate a profile document and save it as user profile. | | **DELETE** `/v1/profile/{kind}/{name}` | Delete a user profile. Returns the profile that applies instead. | The **Profiles** page of EOSdash edits battery profiles as JSON and plots the limit maps and efficiency curves as EOS evaluates them. Edits can be previewed before they are saved. To share a profile with other users, open it on the **Profiles** page and press **Share on GitHub**. It opens a new issue with the battery profile form in your browser, filled in with the profile; check it and submit it. EOS sends nothing itself. Reviewed profiles are packaged with EOS. ### Battery Stati To keep the battery simulation in synchonization with the actual stati of the battery the following resource stati may be reported to EOS by the **PUT** `/v1/resource/status` API endpoint. #### Battery FRBCActuatorStatus The operation mode the battery is currently operated. ```json { "type": "FRBCActuatorStatus", "active_operation_mode_id": "GRID_SUPPORT_IMPORT", "operation_mode_factor": "0.375", "previous_operation_mode_id": "SELF_CONSUMPTION", "transistion_timestamp": "20250725T12:00:12" } ``` #### Battery FRBCStorageStatus The current battery state of charge (SoC). ```json { "type": "FRBCStorageStatus", "present_fill_level": "0.88" } ``` #### Battery PowerMeasurement The current power that the battery is charged or discharged with \[W\]. ```json { "type": "PowerMeasurement", "measurement_timestamp": "20250725T12:00:12", "values": [ { "commodity_quantity": "ELECTRIC.POWER.L1", "value": "887.5" }, { "commodity_quantity": "ELECTRIC.POWER.L2", "value": "905.5" }, { "commodity_quantity": "ELECTRIC.POWER.L2", "value": "1100.7" }, ] } ``` For symmetric (or unknown) power distribution: ```json { "type": "PowerMeasurement", "measurement_timestamp": "20250725T12:00:12", "values": [ { "commodity_quantity": "ELECTRIC.POWER.3_PHASE_SYM", "value": "1000" } ] } ``` ## Electric Vehicle The electric vehicle is basically a battery with a reduced set of operation modes. ### Electric Vehicle Instructions The electric vehicle control instructions assume an idealized EV battery model. Under this model, the EV battery can be operated in two operation modes: | **Operation Mode ID** | **Description** | | --------------------- | ----------------------------------------------------------------------- | | **IDLE** | Battery neither charges nor discharges; holds its state of charge. | | **FORCED_CHARGE** | Charge at a specified power rate up to the allowable maximum. | The **operation mode factor** (0.0–1.0) specifies the normalized power rate relative to the battery's nominal maximum charge power. A value of 1.0 corresponds to full-rate charging, while 0.0 indicates no power transfer. Intermediate values scale the power proportionally. ## Home Appliance The optimization algorithm supports one start of the home appliance within the optimization horizon. ### Home Appliance Simulation ### GENETIC0 Home Appliance Configuration Home appliance to run within the optimization horizon. ```json { "dishwasher1": { "device_id": "dishwasher1", "consumption_wh": 2000, "duration_h": 3 } } ``` Home appliance to run within a time window of 5 hours starting at 8:00 every day and another time window of 3 hours starting at 15:00 every day. See [Time Window Sequence Configuration](configtimewindow-page) for more information. ```json { "dishwasher1": { "device_id": "dishwasher1", "consumption_wh": 2000, "duration_h": 3, "time_windows": { "windows": [ { "start_time": "08:00", "duration": "5 hours" }, { "start_time": "15:00", "duration": "3 hours" } ] } } } ``` :::{admonition} Note :class: note The optimization algorithm always restricts to one start within the optimization horizon per energy management run. ::: ### GENETIC Home Appliance Configuration Home appliance to run once, unconstrained, within the optimization horizon: ```json { "dishwasher1": { "device_id": "dishwasher1", "consumption_wh": 2000, "duration_h": 3, "num_cycles": 1 } } ``` Home appliance to run within a time window of 5 hours starting at 8:00. Each window's `value` field carries the 0-based cycle index it applies to -- here there is only one cycle (index `0`), so `num_cycles` does not need to be set explicitly; it is derived from the distinct cycle indices found in `cycle_time_windows`. See [ime Window Sequence Configuration](configimewindow-page) for more information. ```json { "dishwasher1": { "device_id": "dishwasher1", "consumption_wh": 2000, "duration_h": 3, "cycle_time_windows": { "windows": [ { "start_time": "08:00", "duration": "5 hours", "value": 0 } ] } } } ``` Home appliance that must run **twice** per day, each run confined to its own time window -- cycle 0 in the morning (07:00, 5 hours), cycle 1 in the evening (17:00, 5 hours) -- with at least 1 hour idle between the two runs: ```json { "washing_machine1": { "device_id": "washing_machine1", "consumption_wh": 2000, "duration_h": 2, "min_cycle_gap_h": 1, "cycle_time_windows": { "windows": [ { "start_time": "07:00", "duration": "5 hours", "value": 0 }, { "start_time": "17:00", "duration": "5 hours", "value": 1 } ] } } } ``` `min_cycle_gap_h` enforces a minimum idle time between the end of one cycle and the start of the next; it defaults to `0`, which permits back-to-back runs. To let both cycles run anywhere in a shared window instead of separate morning/evening slots, give both windows the same `start_time` and `duration` but keep their distinct `value` (cycle index) -- the optimizer still places each cycle independently within that shared window. How many cycles remain to be scheduled on a given energy management run is read at runtime from the measurement store, under the key configured in `cycles_completed_measurement_key` (defaults to `{device_id}.cycles_completed`). This lets the optimizer skip cycles the appliance has already completed earlier the same day. :::{admonition} Note :class: note Unlike GENETIC0, the GENETIC algorithm can plan multiple cycles -- and therefore multiple starts -- of the same appliance within a single energy management run, whenever `num_cycles` (or the number of distinct cycle indices in `cycle_time_windows`) is greater than 1. ::: ### Home Appliance Instructions The home appliance instructions assume an idealized home appliance model. Under this model, the home appliance can be operated in two operation modes: | **Operation Mode ID** | **Description** | |-----------------------|-------------------------------------------------------------------------| | **RUN** | The home appliance is started and runs until the end of it's power | | | sequence. | | **OFF** | The home appliance does not run. | The **operation mode factor** (0.0–1.0) is ignored.