mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-11-13 09:13:30 +01:00
An inverter profile describes the technical data of an inverter model: its nominal AC power, the DC→AC and AC→DC efficiency curves over the load fraction (AC power relative to the nominal power), the AC charge power limit of the model, whether that limit also limits PV charging, and an optional power derating over the ambient temperature. Curves are read linearly and clamped at their ends, so every lookup yields a value. The packaged profile generic-hybrid has typical hybrid inverter efficiencies, no nominal power (the inverter device has to provide it) and full power up to 45 degC. Inverter settings name their profile (inverter_profile, default generic-hybrid) and hold the differences of the unit in profile_adjustment: nominal_power_w (replaces the profile value, required for a generic profile), max_power_w and max_ac_charge_power_w (installation limits that can only lower; an AC charge limit of 0 disables AC charging). The effective parameters are resolved at the ambient temperature of the inverter, given by the new measurement key <device_id>-temperature-degc (45 degC without a fresh value); the power derating lowers the maximum power and the AC charge limit. Optimization parameters are always valid: the GENETIC and GENETIC0 parameter preparation resolve the inverter with resolve_current_operating_parameters_or_defaults, which never raises. An unusable profile gives the generic-hybrid profile, a missing nominal power the fallback of 10000 W, an inverter derated to zero power the parameters at 45 degC, and no usable profile at all the profile adjustment with constant efficiency; each is logged as error. The GENETIC inverter takes the AC→DC efficiency curve of the profile, solved exactly on each linear piece of the curve (about 2 % longer runs); GENETIC0 keeps constant efficiencies and no longer replaces the configured inverters with the demo inverter; a battery, electric vehicle or inverter that cannot be prepared is left out of the GENETIC0 run, never replaced by a demo device. Configurations migrate: max_power_w becomes profile_adjustment.nominal_power_w, max_ac_charge_power_w moves into the adjustment, an ac_to_dc_efficiency of 0 keeps AC charging disabled, and the efficiency fields are dropped with an error log, as for batteries, so the profile efficiencies apply. Home Assistant config paths follow. Additional changes - Move the curve algorithms of the battery profile into profilecurve, so battery and inverter profiles share them; the battery profile keeps its document format and classes. - Move ProfileBaseModel, the strict base of profile documents, into profilestore. - Serve inverter profiles through the profile REST API. Refuse to delete a profile that a configured device uses and that has no packaged profile to revert to (409). - Share the measurement access of the device settings in devicemeasurement. - Rename check_battery_profiles to check_device_profiles; the device profile check at configuration setup covers inverters, too. - Document inverter profiles, their configuration, defaults and sharing; regenerate the configuration and OpenAPI documentation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assisted-by: Claude Code (claude-opus-5-5)
534 lines
24 KiB
Markdown
534 lines
24 KiB
Markdown
% 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:
|
||
|
||
<!-- pyml disable line-length -->
|
||
| 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.). |
|
||
<!-- pyml enable line-length -->
|
||
|
||
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/<device_id>` or
|
||
`devices/electric_vehicles/<device_id>`) 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 `<general/data_folder_path>/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 (`battery`, `inverter`). |
|
||
| **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 and inverter profiles as JSON and plots them as EOS
|
||
evaluates them: for a battery the limit maps and efficiency curves, for an inverter the efficiency
|
||
curves and the power derating. Edits can be previewed before they are saved.
|
||
|
||
To share a profile with other users, open it on the **Profiles** page and press **Share**. It
|
||
opens a new issue with the battery or inverter 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.
|
||
|
||
## Inverter
|
||
|
||
The inverter connects the DC side (PV, battery) with the AC side (household, grid). Its
|
||
configuration (`devices/inverters/<device_id>`) is described in the automatic optimization, see
|
||
the inverter simulation configuration there.
|
||
|
||
(inverter-profiles)=
|
||
|
||
### Inverter Profiles
|
||
|
||
The technical data of an inverter comes from an **inverter profile**, a JSON document with:
|
||
|
||
- the DC→AC efficiency curve (battery discharge) and the AC→DC efficiency curve (charging the
|
||
battery from the grid) over the load fraction, the AC power relative to the nominal power,
|
||
- the nominal AC power, except for a generic profile,
|
||
- optionally the AC charge power limit of the model (`max_ac_charge_power_w`) and whether this
|
||
limit also limits PV charging of the battery (`ac_charge_limits_total_charge`, e.g. some hybrid
|
||
inverters in time-of-use grid charging),
|
||
- optionally the power derating over the ambient temperature (`power_derating`: the allowed
|
||
fraction of the nominal power at each temperature).
|
||
|
||
The curves are read linearly between their breakpoints and held at their end values beyond them.
|
||
Unknown keys are rejected.
|
||
|
||
```json
|
||
{
|
||
"inverter_model": "My hybrid inverter",
|
||
"nominal_power_w": 10000,
|
||
"dc_to_ac_efficiency": {
|
||
"load_fraction": [0.05, 0.2, 0.5, 1.0],
|
||
"efficiency": [0.88, 0.955, 0.97, 0.965]
|
||
},
|
||
"ac_to_dc_efficiency": {
|
||
"load_fraction": [0.05, 0.2, 0.5, 1.0],
|
||
"efficiency": [0.86, 0.95, 0.965, 0.96]
|
||
},
|
||
"power_derating": {
|
||
"temperature_degC": [45.0, 60.0],
|
||
"power_fraction": [1.0, 0.6]
|
||
}
|
||
}
|
||
```
|
||
|
||
EOS ships the profile `generic-hybrid` with typical efficiencies of a hybrid inverter, no nominal
|
||
power (set `profile_adjustment.nominal_power_w`) and full power up to 45 °C. EOS evaluates the
|
||
derating at the ambient temperature measured as `<device_id>-temperature-degc`, or at 45 °C
|
||
without a measurement. Inverter profiles are stored, looked up and managed like battery profiles
|
||
(kind `inverter`, user directory `<general/data_folder_path>/profile/inverter/`), with the same
|
||
REST API and the **Profiles** page of EOSdash.
|
||
|
||
The optimization always gets valid inverter parameters. If they cannot be resolved, EOS logs an
|
||
error and uses defaults: the profile `generic-hybrid` for an unusable profile, a nominal power of
|
||
10000 W if none is set, and the parameters at 45 °C if the inverter is derated to zero power at
|
||
the measured temperature.
|
||
|
||
## 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.
|