Skip to content

Instantly share code, notes, and snippets.

@hanzoh
Last active March 3, 2025 20:46
Show Gist options
  • Save hanzoh/51cf37537a9f980e5d0bcfddd624de3d to your computer and use it in GitHub Desktop.
Save hanzoh/51cf37537a9f980e5d0bcfddd624de3d to your computer and use it in GitHub Desktop.
Simulate home battery from Home Assistant data
drop table tmp_battery_sim;
create table tmp_battery_sim (start timestamp without time zone, energy_in_battery numeric, energy_not_consumed_from_grid numeric, savings_from_battery numeric);
do $$
declare
-- Variables
t record;
vn_battery numeric := 0.0;
vn_battery_prev numeric := 0.0;
vn_grid_savings numeric := 0.0;
-- Constants
cv_feedin_sensor varchar := 'sensor.stromzahler_energie_einspeisung';
cv_grid_sensor varchar := 'sensor.stromzahler_energie_bezug';
cn_grid_price numeric := 0.3085;
cn_feedin_comp numeric := 0.0694;
cn_invert_max numeric := 4.0;
cn_efficiency numeric := 0.9;
cn_battery_cap numeric := 26.0;
begin
for t in (
select
to_timestamp(s.start_ts) at time zone 'Europe/Berlin' as start,
-- Rename sensors as to and from grid
case m.statistic_id
when cv_feedin_sensor then 'to_grid'
when cv_grid_sensor then 'from_grid'
end as direction,
coalesce(round(s.state::numeric-lag(s.state) over (partition by m.statistic_id order by s.start_ts)::numeric,3),0.0) as energy
from statistics s
join statistics_meta m
on s.metadata_id = m.id
-- Energy sensors from meter for both directions
where m.statistic_id in (
cv_feedin_sensor,
cv_grid_sensor
)
order by start, direction desc
)
loop
-- Maximum Power of Inverter
if t.energy > cn_invert_max then t.energy := cn_invert_max; end if;
-- Charge Battery
if t.direction = 'to_grid' then
-- Charge/Discharge efficiency combined in one factor
vn_battery := vn_battery + t.energy*cn_efficiency;
-- Battery cannot be charged higher than max capacity
if vn_battery > cn_battery_cap then vn_battery := cn_battery_cap; end if;
end if;
-- Save battery state before discharge
vn_battery_prev := vn_battery;
-- Discharge Battery
if t.direction = 'from_grid' then
vn_battery := vn_battery - t.energy;
-- Batttery cannot be discharged below 0
if vn_battery < 0.0 then vn_battery := 0.0; end if;
end if;
vn_grid_savings := vn_battery_prev-vn_battery;
if t.direction = 'from_grid' then
insert into tmp_battery_sim (start, energy_in_battery, energy_not_consumed_from_grid, savings_from_battery)
values (t.start, vn_battery, vn_grid_savings, vn_grid_savings*(cn_grid_price-cn_feedin_comp));
end if;
end loop;
end$$;
-- Show all calculated data
select start, energy_in_battery, energy_not_consumed_from_grid from tmp_battery_sim;
-- Savings per month
select extract(year from start), extract(month from start), sum(energy_not_consumed_from_grid)
from tmp_battery_sim
group by extract(year from start), extract(month from start)
order by 1,2;
-- Savings per year in Euro
select extract(year from start), sum(energy_not_consumed_from_grid), sum(savings_from_battery)
from tmp_battery_sim
group by extract(year from start)
order by 1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment