Initial commit — energy collector (AlphaEss + SDM630 → TimescaleDB)
This commit is contained in:
140
schema.sql
Normal file
140
schema.sql
Normal file
@@ -0,0 +1,140 @@
|
||||
-- Run once as the TimescaleDB superuser (fitdata):
|
||||
-- psql -h localhost -p 5433 -U fitdata -f schema.sql
|
||||
|
||||
CREATE DATABASE energy;
|
||||
|
||||
\c energy
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS timescaledb;
|
||||
|
||||
CREATE USER energy WITH PASSWORD 'changeme';
|
||||
GRANT ALL ON DATABASE energy TO energy;
|
||||
GRANT ALL ON SCHEMA public TO energy;
|
||||
|
||||
-- ── Raw hypertables ───────────────────────────────────────────────────────────
|
||||
|
||||
-- Written by energy-collector (pvcollect replacement) every 10 s
|
||||
CREATE TABLE inverter (
|
||||
time TIMESTAMPTZ NOT NULL,
|
||||
pv1_power REAL, -- W
|
||||
pv2_power REAL, -- W
|
||||
pv_l1_power REAL, -- W (inverter AC output phase 1)
|
||||
pv_l2_power REAL, -- W
|
||||
pv_l3_power REAL, -- W
|
||||
battery_soc REAL, -- %
|
||||
grid_import_kwh REAL, -- kWh cumulative
|
||||
grid_export_kwh REAL, -- kWh cumulative
|
||||
pv_energy_kwh REAL -- kWh cumulative
|
||||
);
|
||||
SELECT create_hypertable('inverter', 'time', chunk_time_interval => INTERVAL '7 days');
|
||||
|
||||
-- Written by energy-collector (meter replacement) every 10 s, one row per device
|
||||
CREATE TABLE power_meter (
|
||||
time TIMESTAMPTZ NOT NULL,
|
||||
device TEXT NOT NULL, -- 'house' | 'barn'
|
||||
l1_power REAL, -- W
|
||||
l2_power REAL, -- W
|
||||
l3_power REAL, -- W
|
||||
import_kwh REAL, -- kWh cumulative
|
||||
export_kwh REAL -- kWh cumulative
|
||||
);
|
||||
SELECT create_hypertable('power_meter', 'time', chunk_time_interval => INTERVAL '7 days');
|
||||
CREATE INDEX ON power_meter (device, time DESC);
|
||||
|
||||
GRANT ALL ON ALL TABLES IN SCHEMA public TO energy;
|
||||
|
||||
-- ── Continuous aggregates ─────────────────────────────────────────────────────
|
||||
|
||||
-- 10-minute buckets — live chart (1 h, 6 h, 24 h ranges)
|
||||
CREATE MATERIALIZED VIEW inverter_10m
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS
|
||||
SELECT time_bucket('10 minutes', time) AS bucket,
|
||||
AVG(pv1_power + pv2_power) AS pv_power,
|
||||
AVG(battery_soc) AS battery_soc
|
||||
FROM inverter
|
||||
GROUP BY bucket;
|
||||
|
||||
CREATE MATERIALIZED VIEW power_meter_10m
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS
|
||||
SELECT time_bucket('10 minutes', time) AS bucket,
|
||||
device,
|
||||
AVG(l1_power + l2_power + l3_power) AS total_power
|
||||
FROM power_meter
|
||||
GROUP BY bucket, device;
|
||||
|
||||
-- 1-hour buckets — live chart (7 d range), built on 10 m (hierarchical)
|
||||
CREATE MATERIALIZED VIEW inverter_1h
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS
|
||||
SELECT time_bucket('1 hour', bucket) AS bucket,
|
||||
AVG(pv_power) AS pv_power,
|
||||
AVG(battery_soc) AS battery_soc
|
||||
FROM inverter_10m
|
||||
GROUP BY 1;
|
||||
|
||||
CREATE MATERIALIZED VIEW power_meter_1h
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS
|
||||
SELECT time_bucket('1 hour', bucket) AS bucket,
|
||||
device,
|
||||
AVG(total_power) AS total_power
|
||||
FROM power_meter_10m
|
||||
GROUP BY 1, device;
|
||||
|
||||
-- Daily last-value snapshots — basis for bar-chart energy deltas
|
||||
-- Query with LAG() to get per-period consumption.
|
||||
CREATE MATERIALIZED VIEW inverter_daily
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS
|
||||
SELECT time_bucket('1 day', time) AS bucket,
|
||||
last(grid_import_kwh, time) AS grid_import_kwh,
|
||||
last(grid_export_kwh, time) AS grid_export_kwh,
|
||||
last(pv_energy_kwh, time) AS pv_energy_kwh
|
||||
FROM inverter
|
||||
GROUP BY bucket;
|
||||
|
||||
CREATE MATERIALIZED VIEW power_meter_daily
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only = false) AS
|
||||
SELECT time_bucket('1 day', time) AS bucket,
|
||||
device,
|
||||
last(import_kwh, time) AS import_kwh
|
||||
FROM power_meter
|
||||
GROUP BY bucket, device;
|
||||
|
||||
-- ── Retention — keep 30 days of raw data; aggregates stay forever ─────────────
|
||||
|
||||
SELECT add_retention_policy('inverter', INTERVAL '30 days');
|
||||
SELECT add_retention_policy('power_meter', INTERVAL '30 days');
|
||||
|
||||
-- ── Refresh policies ──────────────────────────────────────────────────────────
|
||||
|
||||
SELECT add_continuous_aggregate_policy('inverter_10m',
|
||||
start_offset => INTERVAL '1 hour',
|
||||
end_offset => INTERVAL '10 minutes',
|
||||
schedule_interval => INTERVAL '10 minutes');
|
||||
|
||||
SELECT add_continuous_aggregate_policy('power_meter_10m',
|
||||
start_offset => INTERVAL '1 hour',
|
||||
end_offset => INTERVAL '10 minutes',
|
||||
schedule_interval => INTERVAL '10 minutes');
|
||||
|
||||
SELECT add_continuous_aggregate_policy('inverter_1h',
|
||||
start_offset => INTERVAL '3 hours',
|
||||
end_offset => INTERVAL '1 hour',
|
||||
schedule_interval => INTERVAL '1 hour');
|
||||
|
||||
SELECT add_continuous_aggregate_policy('power_meter_1h',
|
||||
start_offset => INTERVAL '3 hours',
|
||||
end_offset => INTERVAL '1 hour',
|
||||
schedule_interval => INTERVAL '1 hour');
|
||||
|
||||
SELECT add_continuous_aggregate_policy('inverter_daily',
|
||||
start_offset => INTERVAL '3 days',
|
||||
end_offset => INTERVAL '1 day',
|
||||
schedule_interval => INTERVAL '1 day');
|
||||
|
||||
SELECT add_continuous_aggregate_policy('power_meter_daily',
|
||||
start_offset => INTERVAL '3 days',
|
||||
end_offset => INTERVAL '1 day',
|
||||
schedule_interval => INTERVAL '1 day');
|
||||
|
||||
-- Grant SELECT on all tables and views (including continuous aggregates) to energy.
|
||||
-- Run after all views are created so the grant covers them.
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO energy;
|
||||
Reference in New Issue
Block a user