Skip to content

Feature/greedy priority algorithm - #196

Open
carlossg wants to merge 7 commits into
jmcollin78:mainfrom
carlossg:feature/greedy-priority-algorithm
Open

Feature/greedy priority algorithm#196
carlossg wants to merge 7 commits into
jmcollin78:mainfrom
carlossg:feature/greedy-priority-algorithm

Conversation

@carlossg

Copy link
Copy Markdown

Not sure if you are interested but I added a new algorithm based on https://github.com/InventoCasa/ha-advanced-blueprints/blob/main/PV_Excess_Control/README.md and the ability of choosing and compare the algorithms

This algorithm does not model energy costs, it purely maximises solar self-consumption while respecting device priorities

carlossg and others added 4 commits March 11, 2026 14:14
- Add `GreedyPriorityAlgorithm` class: deterministic two-pass algorithm
  that turns on highest-priority devices first within solar surplus, then
  turns off lowest-priority devices on deficit; supports load shedding
  (force-stop lower-priority active devices) when `priority_weight > 0`
  and device has `can_be_shed: true`
- Add `SolarOptimizerAlgorithm` Protocol to enforce a shared interface
  between both algorithms
- Add `CONF_ALGORITHM_TYPE` central config field to select between
  `simulated_annealing` (default) and `greedy_priority`
- Add `can_be_shed` per-device flag to both managed and power-managed
  device schemas
- Wire algorithm selection in coordinator `configure()`
- Add `tests/test_greedy_algo.py`: 9 HA integration tests covering
  algorithm selection, priority ordering, load shedding, and no-shedding
  when `priority_weight=0`
- Add `tests/test_day_simulation.py`: 4 pure-Python fake-day simulation
  tests comparing greedy vs SA over a full synthetic solar day
- Update README.md and README-fr.md: document both algorithms, add
  algorithm selection comparison table, add `algorithm_type` and
  `can_be_shed` to parameter tables, scope SA advanced config as
  SA-only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nvention

- Replace `allowed_power_overage` (watts) with `allowed_power_overage_percent`
  (0-100% slider): overage = solar × percent / 100, so grid import is
  automatically zero at night when solar = 0
- Fix greedy Pass 2 over-shedding: only shed when deficit exceeds
  allowed_power_overage, preserving intentional import from Pass 1
- Fix greedy excess formula: use `excess = -power_consumption` to match
  the real HA sensor convention (net grid consumption, negative = exporting)
- Add `allowed_power_overage` parameter to Protocol, SA (ignored), and
  Greedy (used in Pass 1 turn-on check)
- Fix day simulation test: pass net consumption (gross - solar) so SA
  receives negative values when exporting, matching real HA sensor semantics
- Update greedy integration tests to use net consumption values
- Add `test_day_simulation_three_way_comparison`: SA vs Greedy 0% vs
  Greedy 20% overage with solar utilization % row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents SA vs Greedy Priority behaviour from synthetic day simulation:
objective function mechanics, simulation results table, key findings
(SA ignores priority, Greedy respects ordering, overage percent design),
input convention pitfall, and a when-to-use guide.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jmcollin78

Copy link
Copy Markdown
Owner

Hello,

Yes this is fine for me. I think you can achieve almost the same result with the weight given to the algorithm but it is certainly more simple to have a different algorithm.

I will study that more closely.

@jmcollin78 jmcollin78 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the tranlsations is missing. You add keys so there should be strings.json, en.json and fr.json changed.

Comment thread custom_components/solar_optimizer/config_schema.py
Comment thread custom_components/solar_optimizer/greedy_priority_algo.py Outdated
carlossg and others added 3 commits May 30, 2026 18:18
Add SENSOR_DOMAIN to power entity selector in config schema.
When a non-power-managed device has a power_entity_id configured,
read current power from the sensor state instead of always defaulting
to power_max.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Address PR review comments from @jmcollin78:
- Rename recuit_simule → optimize across the Protocol, both algorithm
  implementations, coordinator call site, and tests
- Make SimulatedAnnealingAlgorithm honour allowed_power_overage: imports
  within that watt threshold are treated as cost-free in calculer_objectif,
  giving SA the same grid-import tolerance as the greedy algorithm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jmcollin78

Copy link
Copy Markdown
Owner

Thank you. I will do complete tests tomorrow before releasing.

@jmcollin78 jmcollin78 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noce PR. I've got one question aboout the changes in managed_device.py

self._can_change_power = self._power_min >= 0
self._convert_power_divide_factor = int(
device_config.get("convert_power_divide_factor") or 1
self._convert_power_divide_factor = (

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change ?
You try to use the real power of a device instead of the power configured for device that cannot change power ?
If I'm right, this is a bad design and not necessary because SO will always adapt itself with the real consumption.

I guess this has nothing to do with the Greedy algo itself. Am I right ?

power_entity_state = self._hass.states.get(self._power_entity_id)
if power_entity_state and power_entity_state.state not in [None, STATE_UNKNOWN, STATE_UNAVAILABLE]:
try:
self._current_power = round(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What the purpose to have power x divide_factor ?
A power is a power. If you x divide_factor the result is not a power (but a Amp for example).

If those changes are not necessary for the Greedy algorithm I suggest to rollback all the managed_device changes.

@carlossg

carlossg commented Jun 7, 2026

Copy link
Copy Markdown
Author

I actually found a deeper issue this week, with devices that are on, but not drawing power, like a thermostat that reached target temperature.

The deeper issue in both algorithms is a model assumption baked into the design:

"active" = consuming power_max for all can_change_power=False devices

This works for a simple binary device — a water heater is either drawing 2200W or 0W. The abstraction holds.

But it breaks for any device where "active" means "permitted to operate" rather than "currently consuming full power." A thermostat-controlled radiator is the clearest example: Solar Optimizer's Active switch being ON means "heating is allowed," not "the element is firing." The actual consumption is controlled by an independent feedback loop (room temp vs. setpoint) that Solar Optimizer has no visibility into.

Both algorithms build their energy balance model from current_power, which for can_change_power=False devices is always power_max. So:

What the algorithm believes What's actually true
vestibulo_superior active → consuming 989W vestibulo_superior active → consuming 0–989W depending on thermal demand
shedding it frees 989W shedding it frees whatever it was actually drawing
net balance after turning on termo: OK net balance: −600W grid import

The root cause is that the integration has no first-class concept of a "permitted but not necessarily consuming" device state. The code models device activity as a two-state system (on = power_max, off = 0), which is insufficient for thermostatic loads, inverter-driven devices, or anything with duty cycling.

Any device that can be "active" at a fraction of its rated power without the optimizer controlling that fraction has this problem in both algorithms.

@jmcollin78

Copy link
Copy Markdown
Owner

I actually found a deeper issue this week, with devices that are on, but not drawing power, like a thermostat that reached target temperature.

This is not an issue for me. If a device in on but not consuming power, then on the next cycle, the available power will be high and next devices could be started. Nothing wrong with the actual algorithm.
The max power is a max and cannot be reach. That makes no difference after a while.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants