https://github.com/ttlucian/ha-solar-ac-controller
A smart and adaptive Home Assistant integration that manages multi-zone (milti-split) AC systems based on real-time solar production, grid import/export, and learned compressor behavior.
https://github.com/ttlucian/ha-solar-ac-controller
custom-integration home-assistant home-assistant-integration integration
Last synced: 3 months ago
JSON representation
A smart and adaptive Home Assistant integration that manages multi-zone (milti-split) AC systems based on real-time solar production, grid import/export, and learned compressor behavior.
- Host: GitHub
- URL: https://github.com/ttlucian/ha-solar-ac-controller
- Owner: TTLucian
- License: mit
- Created: 2026-01-01T14:47:32.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-22T17:22:15.000Z (4 months ago)
- Last Synced: 2026-02-22T22:24:54.917Z (4 months ago)
- Topics: custom-integration, home-assistant, home-assistant-integration, integration
- Language: Python
- Homepage: https://github.com/TTLucian/ha-solar-ac-controller
- Size: 982 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# π Solar AC Controller β Home Assistant Integration
A smart and adaptive Home Assistant integration that manages multi-zone (multi-split) AC systems based on real-time solar production, grid import/export, and learned compressor behavior.
This integration automatically:
- **Dynamically controls AC zones** based on available solar export and grid conditions
- **Learns each zone's power consumption** using an adaptive EMA (Exponential Moving Average) algorithm, storing separate *lead* and *extension* values so cold-start estimates are never overwritten by concurrent-zone measurements
- **Prevents short-cycling** with configurable delays for both ON and OFF transitions
- **Detects manual overrides** and locks zones to respect user control
- **Performs panic shedding** when grid import exceeds configurable thresholds
- **Optional master AC switch control** based on solar production thresholds
- **Comfort-aware zone control** with per-zone temperature sensors for intelligent removal prioritization
- **Exposes comprehensive diagnostics** through sensors and JSON export
- **Provides runtime reconfiguration** via Options Flow without restart
- **Runtime control entities** β pause the control loop, toggle activity logging, and adjust aggressiveness live without entering the config flow
Designed as a Home Assistant **service integration** for high-performance, solar-aware HVAC automation.
---
[](https://www.buymeacoffee.com/TTLucian)
---
## π¦ Installation
### Manual Installation
1. Copy the `custom_components/solar_ac_controller` folder into your Home Assistant `config/custom_components/` directory
2. Restart Home Assistant
3. Add the integration via **Settings β Devices & Services β Add Integration β Solar AC Controller**
### HACS Installation (Custom Repository)
1. Open **HACS β Integrations**
2. Click the **three-dot menu** β **Custom repositories**
3. Add repository:
- **URL:** `https://github.com/TTLucian/ha-solar-ac-controller`
- **Category:** Integration
4. Click **Install** on the Solar AC Controller card
5. Restart Home Assistant
6. Add the integration via **Settings β Devices & Services β Add Integration**
### Quick Links
[](https://my.home-assistant.io/redirect/hacs_repository/?owner=TTLucian&repository=ha-solar-ac-controller&category=integration)
[](https://my.home-assistant.io/redirect/config_flow_start?domain=solar_ac_controller)
## π Features
### π Solar-aware zone control
Zones activate **in priority order** (based on config order) using real-time solar export and grid import data. The controller maintains separate EMAs (30-second and 5-minute) for responsive yet stable decision-making.
### π§ Adaptive learning engine
Each zone's power consumption is learned using a **per-mode (heat/cool) EMA model** with bootstrap initialization. The system tracks samples and continuously refines estimates as zones operate, improving accuracy over time.
The engine distinguishes **lead** from **extension** measurements. The first time a zone turns on (cold-start, from no active zones) the observed power draw is stored separately as the *lead* value. Subsequent extension turn-ons (while other zones are already running) are stored under the regular key and never overwrite the lead value. When estimating whether there is enough solar to start from zero the controller prefers the lead value, preventing systematic under-estimation caused by zone interactions.
### π Manual override detection
When a zone state changes outside the controller's actions, a **configurable lock** (default 20 minutes) prevents the controller from modifying that zone, respecting user intent.
### π Panic shedding
When grid import exceeds a configured threshold **and persists for the panic delay**, the controller **sequentially sheds zones** (with configurable inter-action delays) to protect the inverter from overload. The **highest-priority zone is intentionally kept running** during the first shed pass β it is only removed if a second panic fires while the cooldown is still active.
### π Optional master switch control
If configured, the controller can automatically turn the master AC switch ON when solar production exceeds `solar_threshold_on` and OFF when it drops below `solar_threshold_off`, using hysteresis to prevent oscillation.
### π Full observability
Exposes **20+ sensors and binary sensors** showing EMAs, confidence scores, zone states, panic status, learning activity, and more. **Three runtime control entities** (Aggressiveness number, Season Mode select, Integration Enable switch) allow hands-on tuning without opening the config flow. Optional diagnostics sensor provides complete internal state as JSON attributes.
---
## π‘ Exposed Entities
### **Sensors**
- **Active Zones** β Comma-separated list of currently running zones
- **Active Zone Count** β Number of currently active zones (useful for automations)
- **Next Zone** β The zone that will be added next if conditions allow
- **Last Zone** β The most recently active zone
- **Last Action** β Most recent controller action (e.g., `added_zone`, `removed_zone`, `panic`, `no_action`)
- **EMA 30s** β 30-second exponential moving average of grid power
- **EMA 5m** β 5-minute exponential moving average of grid power
- **Confidence** β Current decision confidence score (points)
- **Confidence Thresholds** β Shows unified add/remove confidence thresholds as attributes
- **Required Export** β Minimum export needed to add the next zone
- **Required Export Source** β Reason code for the current required export value (translated ENUM: `Learned Power`, `Manual Power Override`, `Panic Recovery`, `Integration Disabled`, `Solar Freeze`, `Initializing`)
- **Export Margin** β Current export headroom above required export
- **Learned Idle Power** β Learned compressor draw while running with no active zones
- **Grid Import Tolerance** β Current import tolerance in W, live-updated from the Aggressiveness slider (`aggressiveness Γ 700 W`)
- **Compressor Recovery Remaining** β Seconds left on the post-add compressor ramp-up guard
- **Samples** β Number of learning samples collected
- **Last Relearn** (diagnostic) β Timestamp of the most recent `force_relearn` call; the `target` attribute contains the zone that was reset (`"all"` when every zone was cleared)
- **Learned Power [zone]** β Per-zone learned power consumption (one sensor per configured zone); reflects the *extension* learned value β use `Peak Delta` to understand the cold-start lead draw
- **Zone Lock Remaining [zone]** β Seconds until a zone's manual-override lock expires (one per zone)
- **Add Confidence Breakdown** (optional diagnostic) β Per-factor breakdown of the add confidence score
- **Remove Confidence Breakdown** (optional diagnostic) β Per-factor breakdown of the remove confidence score
- **Peak Delta [zone]** (optional diagnostic) β Learned compressor startup surge per zone (lead β extension draw)
- **Diagnostics** (optional) β JSON snapshot of complete controller state
### **Binary Sensors**
- **Learning Active** β Whether a learning cycle is in progress
- **Panic State** β Whether panic shedding is currently active
- **Panic Cooldown** β Whether panic cooldown period is active
- **Short Cycling** β Whether any zone is in short-cycle protection
- **Manual Lock Active** β Whether any zone is manually locked
- **Exporting** β Grid export active (EMA 30s < 0)
- **Importing** β Grid import active (EMA 30s > 0)
- **Master Switch** β State of the optional master AC switch
### **Controls**
- **Aggressiveness** (number, 0.0β1.0) β Live slider to tune activation eagerness; see [Aggressiveness Slider](#οΈ-aggressiveness-slider). Persisted across restarts.
- **Season Mode** (select, `heat`/`cool`) β Change the operating season without entering the config flow. Takes effect immediately.
- **Integration Enable** (switch) β Pause the entire control loop without unloading the integration. Useful during manual maintenance or testing.
- **Activity Logging** (switch, config category) β Toggle verbose logbook entries for zone adds, removes, panic events, and learning cycles.
---
## π Diagnostics
The Solar AC Controller provides a unified diagnostics system designed to help with troubleshooting, performance tuning, and understanding the controllerβs internal decision engine. Diagnostics are available in two complementary forms:
### π§ Diagnostics Sensor (Optional)
You can enable an alwaysβon diagnostics sensor that exposes the controllerβs full internal state as JSON attributes.
**How to enable:**
1. Go to **Settings β Devices & Services β Solar AC Controller β Configure**
2. Toggle **Enable Diagnostics Sensor**
3. Submit the form
When enabled, Home Assistant will create:
```Code
sensor.solar_ac_diagnostics
```
This entity updates in real time and includes:
- Controller configuration
- Learned power values
- EMA metrics (30s and 5m)
- Active and lastβused zones
- Decision engine state (next zone, last action, required export, margin)
- Panic thresholds and cooldown state
- Master switch lockout timers
- Timestamps and runtime counters
This sensor is intended for advanced users, debugging, and Lovelace dashboards.
### π Home Assistant Diagnostics Export (Always Available)
Even if the diagnostics sensor is disabled, you can always download a full diagnostics report:
Settings β Devices & Services β Solar AC Controller β Download Diagnostics
This export contains the same structured data as the diagnostics sensor, generated through the same internal helper. It includes:
- Timestamp
- Full configuration (merged data + options)
- Learning state and samples
- EMA values
- Decision engine outputs
- Zone activity and lockouts
- Panic state
- Master switch state
No personal or sensitive data is included.
### π§© Unified Diagnostics Architecture
Both the diagnostics sensor and the HA diagnostics export use the same internal function:
```Code
build_diagnostics(coordinator)
```
This ensures:
- Identical data in both places
- No duplication of logic
- No risk of the two drifting apart
- Zero dependency between the sensor and the export
Disabling the diagnostics sensor does not affect the JSON diagnostics export.
### π Privacy
The diagnostics system intentionally excludes:
- User identity
- Location
- Energy usage history
- Any personally identifiable information
Only integration configuration and runtime controller state are included.
---
## βοΈ Configuration
### Initial Setup
Add the integration via:
**Settings β Devices & Services β Add Integration β Solar AC Controller**
### Required Configuration
- **Solar sensor** β Entity measuring solar production (W)
- **Grid sensor** β Entity measuring grid power (W, positive=import, negative=export)
- **AC power sensor** β Entity measuring total AC power consumption (W)
- **Zones** β Multi-select of `climate`, `switch`, or `fan` entities (order = priority)
### Optional Configuration
- **Master AC switch** β Optional switch entity to control entire AC system
- **Solar ON threshold** (default: 1200W) β Solar production required to enable master switch
- **Solar OFF threshold** (default: 500W) β Solar production below which master switch turns off
- **Panic threshold** (default: 2000W) β Grid import level triggering panic shedding
- **Panic delay** (default: 60s) β How long panic condition must persist
- **Manual lock seconds** (default: 1200s) β Duration zones are locked after manual changes
- **Short cycle ON seconds** (default: 1200s) β Minimum ON time before allowing OFF
- **Short cycle OFF seconds** (default: 20s) β Minimum OFF time before allowing ON
- **Action delay seconds** (default: 3s) β Delay between consecutive service calls
- **Initial learned power** (default: 1000W) β Bootstrap estimate before learning completes
- **Max temperature winter** (default: 21C) β Comfort target for zones in heat mode
- **Min temperature summer** (default: 21C) β Comfort target for zones in cool mode
- **Zone temperature sensors** (optional) β Per-zone indoor temperature sensor entities for comfort-aware removal blocking
- **Zone manual power** (optional) β Comma-separated per-zone power overrides (W) to bypass learned power for specific zones; format: `zone_id:watts, zone_id:watts`
- **Enable temperature modulation** (default: enabled) β Allow the controller to use temperature data to influence zone removal decisions
- **Update interval** (default: 10s) β How often the control loop runs (seconds)
- **Compressor ramp seconds** (default: 600s) β Time after a zone is added during which the compressor is considered to be ramping up; used to suppress premature removal decisions
- **Enable diagnostics sensor** (default: disabled) β Optional JSON diagnostics sensor
- **Aggressiveness** (default: 0.5) β Controls how eagerly zones are activated; see the [Aggressiveness Slider](#οΈ-aggressiveness-slider) section for details
---
## π Runtime Options (Options Flow)
All configuration parameters can be changed at runtime via **Settings β Devices & Services β Solar AC Controller β Configure**.
When launched via Reconfigure, the form now pre-fills with your existing data+options values for a faster review.
### Behavioral Parameters
- **`manual_lock_seconds`** β Duration a zone remains locked after manual override (default: 1200s / 20 min)
- **`short_cycle_on_seconds`** β Minimum ON time before allowing OFF transition (default: 1200s)
- **`short_cycle_off_seconds`** β Minimum OFF time before allowing ON transition (default: 20s)
- **`action_delay_seconds`** β Inter-service-call delay for sequential zone actions (default: 3s)
- **`update_interval`** β Control loop frequency in seconds (default: 10s)
- **`compressor_ramp_seconds`** β Ramp-up guard window after a zone is added (default: 600s / 10 min)
### Threshold Parameters (Watts)
- **`panic_threshold`** β Grid import level triggering panic shedding (default: 2000W)
- **`panic_delay`** β Persistence time before panic activates (default: 60s)
- **`solar_threshold_on`** β Solar production to enable master switch (default: 1200W)
- **`solar_threshold_off`** β Solar production to disable master switch (default: 500W)
### Decision Engine Parameters
- **`initial_learned_power`** β Bootstrap estimate before learning completes (default: 1000W)
- **`aggressiveness`** β Zone activation eagerness from 0.0 (conservative) to 1.0 (aggressive); drives add/remove thresholds and import tolerance simultaneously (default: 0.5). Also adjustable in real time via the **Aggressiveness** number entity.
### Comfort Parameters
- **`max_temp_winter`** β Comfort ceiling for zones in heat mode; zones at or above this temperature are deprioritised for removal protection (default: 21Β°C)
- **`min_temp_summer`** β Comfort floor for zones in cool mode (default: 21Β°C)
- **`zone_temp_sensors`** β Per-zone temperature sensor entity IDs; required for non-climate zones when temperature modulation is enabled
- **`zone_manual_power`** β Per-zone power overrides; bypasses learned power for the named zones
- **`enable_temperature_modulation`** β Use temperature data in zone removal decisions (default: enabled)
### Diagnostics
- **`enable_diagnostics_sensor`** β Toggle optional diagnostics sensor (default: disabled)
**Changes apply immediately** after saving β no integration reload required.
---
## ποΈ Aggressiveness Slider
The **Aggressiveness** slider (range 0.0 β 1.0) is the primary tuning knob for how eagerly the controller activates and keeps zones running. Moving it drives **four interconnected parameters simultaneously**, so you never need to manually edit confidence thresholds or import tolerance to change the overall behaviour.
### Parameters driven by aggressiveness
| Parameter | Formula | Conservative (0.0) β Aggressive (1.0) |
|---|---|---|
| **Add threshold** | `80 β 60 Γ a` | 80 pts β 20 pts |
| **Remove threshold** | `β70 + 50 Γ a` *(clamped for 50-pt deadband)* | β70 pts β β30 pts |
| **Grid import tolerance** | `a Γ 700 W` | 0 W β 700 W |
| **Deadband** *(add β remove)* | 150 β 100 Γ a *(β₯ 50 pts)* | 150 pts β 50 pts |
**What each parameter does:**
- **Add threshold** β Minimum confidence score the controller must reach before switching a zone on. Lower means easier to turn on.
- **Remove threshold** β Confidence score must fall below this (negative) value before the controller turns a zone off. Less negative means easier to turn off.
- **Grid import tolerance** β Watts of grid import the controller accepts while still allowing zone activation. Lets the system be optimistic during short demand spikes at the moment a compressor starts.
- **Deadband** β Gap between the add and remove thresholds. A wide deadband resists oscillation; a narrow one reacts faster to changing conditions.
### Settings reference table
| Aggressiveness | Add threshold (pts) | Remove threshold (pts) | Grid tolerance (W) | Deadband (pts) | Behaviour summary |
|:-:|:-:|:-:|:-:|:-:|:--|
| **0.0** | 80 | β70 | 0 | 150 | Maximum caution β zones only activate during strong, sustained export; no grid import tolerated at all |
| **0.1** | 74 | β65 | 70 | 139 | Very conservative; tiny transient import (70 W) tolerated |
| **0.2** | 68 | β60 | 140 | 128 | Conservative; requires reliable export before activating |
| **0.3** | 62 | β55 | 210 | 117 | Slightly cautious; small 210 W import margin accepted |
| **0.4** | 56 | β50 | 280 | 106 | Below default; suits smaller solar arrays or shared inverters |
| **0.5** | 50 | β45 | 350 | 95 | **Default** β balanced; tolerates ~350 W transient import during compressor ramp-up |
| **0.6** | 44 | β40 | 420 | 84 | Slightly aggressive; zones activate more readily on moderate export |
| **0.7** | 38 | β35 | 490 | 73 | Aggressive; accepts up to 490 W of import |
| **0.8** | 32 | β30 | 560 | 62 | Very aggressive; turns on with modest export |
| **0.9** | 26 | β25 | 630 | 51 | Near-maximum; minimal solar surplus needed to trigger activation |
| **1.0** | 20 | β30 ΒΉ | 700 | 50 | Maximum β activates on any measurable surplus; 50-pt minimum deadband enforced |
> ΒΉ At aggressiveness 1.0 the raw remove threshold (β20) would reduce the deadband below 50 points, so it is clamped to β30 to preserve stability and prevent rapid cycling.
### Choosing a value
| Situation | Recommended range |
|---|---|
| Cloudy / unreliable generation or shared inverter | 0.2 β 0.4 |
| Typical rooftop solar system | **0.5 (default)** |
| Large array, modest AC loads | 0.6 β 0.7 |
| Maximise self-consumption, large array | 0.8 β 1.0 |
---
## π§© Services
### `solar_ac_controller.force_relearn`
Resets learned power values and sample count for specific zones or all zones. This allows you to force the system to relearn power consumption patterns.
**Parameters:**
- `zone` (optional): Specific climate or switch zone entity ID to reset. If omitted, resets all zones.
**Examples:**
Reset all zones (full relearn):
```yaml
action: solar_ac_controller.force_relearn
data: {}
```
Reset specific zone:
```yaml
action: solar_ac_controller.force_relearn
data:
zone: climate.living_room
```
## π Credits
**Created by:** [@TTLucian](https://github.com/TTLucian)
### Device Version in Home Assistant
The integration creates a **single logical device** ("Solar AC Controller") that manages all zones. The device version in Home Assistant Settings β Devices & Services comes from **[manifest.json](custom_components/solar_ac_controller/manifest.json)** (the `version` field).
- **Device Version = Integration Version** (shown in HA UI)
- If you see an old version after updating, restart Home Assistant or delete and re-add the integration
- Version is fetched at setup time from the manifest
Designed for high-performance, solar-aware HVAC automation with comprehensive observability and production-grade reliability.