Skip to main content

Overview

Simulation mode generates realistic solar production and household load data based on time-of-day patterns, allowing you to test Energy Control Pro’s features without connecting to actual hardware sensors. This is invaluable for development, testing automations, and understanding system behavior.
Simulation mode is the default when you first install Energy Control Pro.

Enabling Simulation Mode

Configuration Key (const.py:7):
CONF_SIMULATION = "simulation"
Default Behavior (coordinator.py:100-107):
simulation = self._entry.options.get(
    CONF_SIMULATION,
    self._entry.data.get(CONF_SIMULATION, True),
)
profile = self._entry.options.get(
    CONF_PROFILE,
    self._entry.data.get(CONF_PROFILE, PROFILE_SUNNY_DAY),
)
Set simulation: true in your configuration to enable simulation mode.

Simulation Profiles

Three pre-configured profiles simulate different weather and seasonal conditions:

Sunny Day (Default)

Constant (const.py:76):
PROFILE_SUNNY_DAY = "sunny_day"
Tuning Parameters (logic.py:24-31):
PROFILE_SUNNY_DAY: ProfileTuning(
    solar_peak_w=6200,
    day_length_h=14,
    cloud_variability=0.06,
    load_base_w=950,
    load_evening_boost_w=850,
)

Sunny Day Characteristics

  • Peak Solar: 6200W
  • Daylight Hours: 14 hours (5am - 7pm)
  • Cloud Variability: 6% (very stable)
  • Base Load: 950W
  • Evening Peak: +850W (19:00 centered)

Cloudy Day

Constant (const.py:77):
PROFILE_CLOUDY_DAY = "cloudy_day"
Tuning Parameters (logic.py:32-38):
PROFILE_CLOUDY_DAY: ProfileTuning(
    solar_peak_w=3600,
    day_length_h=12,
    cloud_variability=0.20,
    load_base_w=1050,
    load_evening_boost_w=800,
)

Cloudy Day Characteristics

  • Peak Solar: 3600W (58% of sunny day)
  • Daylight Hours: 12 hours (6am - 6pm)
  • Cloud Variability: 20% (significant fluctuations)
  • Base Load: 1050W
  • Evening Peak: +800W

Winter Day

Constant (const.py:78):
PROFILE_WINTER_DAY = "winter_day"
Tuning Parameters (logic.py:39-45):
PROFILE_WINTER_DAY: ProfileTuning(
    solar_peak_w=2400,
    day_length_h=9,
    cloud_variability=0.15,
    load_base_w=1250,
    load_evening_boost_w=1000,
)

Winter Day Characteristics

  • Peak Solar: 2400W (39% of sunny day)
  • Daylight Hours: 9 hours (7:30am - 4:30pm)
  • Cloud Variability: 15% (moderate fluctuations)
  • Base Load: 1250W (higher heating load)
  • Evening Peak: +1000W (increased evening usage)

Simulation Algorithm

The simulation engine generates power values based on current time and profile characteristics:

Core Simulation Function

Function Signature (logic.py:53-62):
def simulate(
    profile: str,
    now: datetime,
    *,
    cloud_noise: float | None = None,
    appliance_noise: float | None = None,
    profile_tuning: dict[str, ProfileTuning] | None = None,
) -> tuple[int, int]:
    """Simulate solar/load power in W for a profile at a specific instant."""

Solar Generation Model

Implementation (logic.py:63-80):
tuning_map = profile_tuning or PROFILE_TUNING
tuning = tuning_map.get(profile, tuning_map[PROFILE_SUNNY_DAY])

sunrise = 12 - (tuning.day_length_h / 2)
sunset = 12 + (tuning.day_length_h / 2)
hour = now.hour + (now.minute / 60) + (now.second / 3600)

if sunrise <= hour <= sunset:
    phase = (hour - sunrise) / max((sunset - sunrise), 0.1)
    daylight_factor = math.sin(math.pi * phase)
else:
    daylight_factor = 0.0

cloud_noise_val = (
    cloud_noise
    if cloud_noise is not None
    else random.uniform(-tuning.cloud_variability, tuning.cloud_variability)
)
solar_w = max(0, int(tuning.solar_peak_w * daylight_factor * (1 + cloud_noise_val)))
1

Calculate Sunrise/Sunset

Sunrise and sunset times are centered at noon based on day_length_h
2

Compute Daylight Factor

Use sine curve to model solar intensity: sin(π * phase) where phase ranges 0→1 from sunrise to sunset
3

Apply Cloud Variability

Add random noise within ±cloud_variability to simulate cloud cover
4

Calculate Solar Output

Multiply solar_peak_w * daylight_factor * (1 + noise)

Solar Curve Shape

The sine curve produces a realistic bell-shaped generation curve that peaks at solar noon and tapers to zero at sunrise/sunset.

Load Consumption Model

Implementation (logic.py:82-92):
morning_peak = 250 * math.exp(-((hour - 7.5) ** 2) / 3.0)
evening_peak = tuning.load_evening_boost_w * math.exp(-((hour - 19.0) ** 2) / 4.5)
appliance_noise_val = (
    appliance_noise if appliance_noise is not None else random.uniform(-120, 180)
)
load_w = max(
    200,
    int(tuning.load_base_w + morning_peak + evening_peak + appliance_noise_val),
)

return solar_w, load_w
1

Base Load

Constant baseline consumption from always-on appliances (load_base_w)
2

Morning Peak

Gaussian curve centered at 7:30am with 250W peak (breakfast, showers)
3

Evening Peak

Gaussian curve centered at 7:00pm with profile-specific boost (cooking, lighting, entertainment)
4

Appliance Noise

Random variation (±120-180W) to simulate appliances turning on/off
5

Minimum Floor

Ensure load never drops below 200W

Data Structure

ProfileTuning Dataclass

Definition (logic.py:13-22):
@dataclass(frozen=True)
class ProfileTuning:
    """Tuning values for generation/load simulation."""

    solar_peak_w: float
    day_length_h: float
    cloud_variability: float
    load_base_w: float
    load_evening_boost_w: float
Maximum solar generation in watts when sun is at peak position (noon) with clear skies
Total daylight hours, centered at noon. Affects sunrise and sunset times.
Fractional random variation applied to solar output (0.06 = ±6%)
Constant baseline household consumption throughout the day
Additional consumption peak in evening hours (centered at 7pm)

Simulation Invocation

Coordinator Entry Point (coordinator.py:109-112):
if simulation:
    data = self._simulate_values(profile, now=now)
else:
    data = self._real_values_from_entities()
Simulation Wrapper (coordinator.py:390-393):
def _simulate_values(self, profile: str, *, now: datetime) -> dict[str, int]:
    """Generate realistic-ish power values for the selected profile."""
    solar_w, load_w = simulate(profile, now=now)
    return calculate_balance(solar_w, load_w)
Simulation calls calculate_balance() just like real mode, ensuring consistent data structure regardless of source.

Testing Different Times

Simulation uses the current system time, so you can observe different behaviors throughout the day:
  • Low solar production (rising)
  • Morning load peak
  • Likely importing from grid

Profile Comparison

Visual comparison of profiles at solar noon:
MetricSunny DayCloudy DayWinter Day
Solar Peak6200W3600W2400W
% of Sunny100%58%39%
Daylight14h12h9h
Variability±6%±20%±15%
Base Load950W1050W1250W
Evening Peak+850W+800W+1000W

Switching to Real Mode

When ready to use actual hardware:
1

Configure Entities

Set solar_power_entity and load_power_entity to your actual sensors
2

Disable Simulation

Set simulation: false in configuration
3

Reload Integration

Reload Energy Control Pro or restart Home Assistant
4

Verify Data

Check that sensors show realistic values from your hardware
Real Mode Implementation (coordinator.py:145-157):
def _real_values_from_entities(self) -> dict[str, int]:
    """Read solar/load from mapped entities and derive all metrics in W."""
    solar_entity_id = str(self._get_option(CONF_SOLAR_POWER_ENTITY, "") or "")
    load_entity_id = str(self._get_option(CONF_LOAD_POWER_ENTITY, "") or "")

    if not solar_entity_id or not load_entity_id:
        raise UpdateFailed(
            "Real mode requires solar_power_entity and load_power_entity in options"
        )

    solar_w = self._read_power_w(solar_entity_id)
    load_w = self._read_power_w(load_entity_id)
    return calculate_balance(solar_w, load_w)

Use Cases

Development

Test optimization logic without waiting for actual solar conditions

Automation Testing

Verify automations trigger correctly at different times of day

UI Development

Build dashboards and visualizations with consistent data

Training

Demonstrate system behavior to users without hardware

Simulation Determinism

Simulation includes random noise for realism:
  • Cloud variability: Random within ±cloud_variability
  • Appliance noise: Random between -120W and +180W
For deterministic testing, pass explicit noise values:
solar_w, load_w = simulate(
    "sunny_day",
    now=datetime(2026, 3, 4, 12, 0),
    cloud_noise=0.0,        # No cloud variation
    appliance_noise=0.0,     # No appliance variation
)
The coordinator always uses random noise in normal operation. Deterministic mode is only available when calling simulate() directly (e.g., in tests).

Example Configuration

# Enable simulation mode with cloudy day profile
simulation: true
profile: cloudy_day

# Optional: configure thresholds for testing alerts
export_threshold_w: 500
import_threshold_w: 300
duration_threshold_min: 5

Debugging Simulation

View current simulated values in Home Assistant:
type: entities
entities:
  - sensor.solar_power
  - sensor.load_power
  - sensor.surplus_power
  - sensor.grid_import_power
  - sensor.grid_export_power
  - sensor.energy_state
Or check the coordinator data in Developer Tools > States:
sensor.solar_power
  state: 4523
  attributes:
    unit_of_measurement: W
    device_class: power
    icon: mdi:solar-power

Next Steps

Energy Monitoring

Learn about the sensors populated by simulation

Load Optimization

Test optimization with simulated data

Configuration

Configure simulation profiles and parameters

Real Mode Setup

Switch to actual hardware sensors

Build docs developers (and LLMs) love