Files
energy-collector/main.go
Thomas Klaehn 62ffd06444 Add go-e wallbox charger integration
- charger.go: polls go-e /api/status?filter=nrg,eto every 10 s
- db.go: WriteCharger() inserts into charger hypertable
- config.go: ChargerConf with host field
- main.go: polls charger in parallel with inverter and meters
- schema.sql: charger table + charger_10m/1h/daily aggregates + policies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 11:30:59 +02:00

123 lines
2.4 KiB
Go

package main
import (
"context"
"flag"
"log"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
var logger = log.New(os.Stderr, "", log.Ltime|log.Lshortfile)
func main() {
cfgPath := flag.String("c", "/srv/energy/collector/config.json", "path to config file")
flag.Parse()
cfg, err := loadConfig(*cfgPath)
if err != nil {
log.Fatalf("config: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := NewDB(ctx, cfg.DB.DSN)
if err != nil {
log.Fatalf("db: %v", err)
}
defer db.Close()
alpha, err := NewAlphaEss(cfg.AlphaEss)
if err != nil {
log.Fatalf("alphaess: %v", err)
}
defer alpha.Close()
meters, err := NewMeterPoller(cfg.MQTT)
if err != nil {
log.Fatalf("meters: %v", err)
}
defer meters.Close()
interval := time.Duration(cfg.SampleRate) * time.Second
go runPollLoop(ctx, alpha, meters, cfg.Charger.Host, db, interval)
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
logger.Println("shutting down")
}
func runPollLoop(ctx context.Context, alpha *AlphaEss, meters *MeterPoller, chargerHost string, db *DB, interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
poll(ctx, alpha, meters, chargerHost, db)
}
}
}
func poll(ctx context.Context, alpha *AlphaEss, meters *MeterPoller, chargerHost string, db *DB) {
t := time.Now()
var (
inverterData *InverterData
meterData []MeterReading
chargerData *ChargerReading
wg sync.WaitGroup
)
wg.Add(3)
go func() {
defer wg.Done()
d, err := alpha.Poll()
if err != nil {
logger.Printf("alphaess: %v", err)
return
}
inverterData = d
}()
go func() {
defer wg.Done()
meterData = meters.PollAll()
}()
go func() {
defer wg.Done()
if chargerHost == "" {
return
}
r, err := PollCharger(chargerHost)
if err != nil {
logger.Printf("charger: %v", err)
return
}
r.Time = t
chargerData = &r
}()
wg.Wait()
if inverterData != nil {
if err := db.WriteInverter(ctx, t, inverterData); err != nil {
logger.Printf("write inverter: %v", err)
}
}
for _, r := range meterData {
if err := db.WriteMeter(ctx, t, r); err != nil {
logger.Printf("write meter %s: %v", r.Device, err)
}
}
if chargerData != nil {
if err := db.WriteCharger(ctx, *chargerData); err != nil {
logger.Printf("write charger: %v", err)
}
}
}