From 851c6d01ecdcec79c2ee9bcf2a402c790d1fd184 Mon Sep 17 00:00:00 2001 From: Pieter Hollander Date: Sat, 4 Oct 2025 01:03:49 +0200 Subject: [PATCH] Expand hints. Add RadioStatistics struct. Add new Order, DataAge and DataAgeMs. --- main.go | 61 +++++++++++++++++--------- migrations/00009_hints_pin_mapping.sql | 9 ++++ 2 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 migrations/00009_hints_pin_mapping.sql diff --git a/main.go b/main.go index 295ed2a..b6041cf 100644 --- a/main.go +++ b/main.go @@ -72,20 +72,34 @@ type InverterINV struct { YieldTotal VUD `json:"YieldTotal"` } +// RadioStatistics contains radio communication statistics +type RadioStatistics struct { + TxRequest int `json:"tx_request"` + TxReRequest int `json:"tx_re_request"` + RxSuccess int `json:"rx_success"` + RxFailNothing int `json:"rx_fail_nothing"` + RxFailPartial int `json:"rx_fail_partial"` + RxFailCorrupt int `json:"rx_fail_corrupt"` + RSSI float64 `json:"rssi"` +} + // Inverter struct type Inverter struct { Serial string `json:"serial"` Name string `json:"name"` + Order int `json:"order"` + DataAge int `json:"data_age"` + DataAgeMs int `json:"data_age_ms"` Producing bool `json:"producing"` LimitRelative float64 `json:"limit_relative"` LimitAbsolute float64 `json:"limit_absolute"` - AC map[string]InverterAC `json:"AC"` - DC map[string]InverterDC `json:"DC"` - Events int `json:"events"` PollEnabled bool `json:"poll_enabled"` Reachable bool `json:"reachable"` - DataAge int `json:"data_age"` + Events int `json:"events"` + AC map[string]InverterAC `json:"AC"` + DC map[string]InverterDC `json:"DC"` INV map[string]InverterINV `json:"INV"` + RadioStats RadioStatistics `json:"radio_stats"` } type Total struct { @@ -98,6 +112,7 @@ type Hints struct { TimeSync bool `json:"time_sync"` RadioProblem bool `json:"radio_problem"` DefaultPassword bool `json:"default_password"` + PinMappingIssue bool `json:"pin_mapping_issue"` } type LiveData struct { @@ -165,7 +180,7 @@ var config Config // LoadConfig attempts to read the configuration from options.json // If it fails, it falls back to using environment variables -func loadConfig() Config { +func loadConfig() (Config, error) { configFilePath := os.Getenv("CONFIG_FILE") if configFilePath == "" { configFilePath = "/data/options.json" @@ -176,20 +191,20 @@ func loadConfig() Config { // Successfully read the file, parse the JSON err = json.Unmarshal(data, &config) if err != nil { - log.Fatalf("Error parsing config file: %v", err) + return Config{}, fmt.Errorf("error parsing config file: %w", err) } if config.DB == "" { - log.Fatal("db connection settings are not set") + return Config{}, fmt.Errorf("db connection settings are not set") } if config.OpenDTUAddress == "" { - log.Fatal("opendtu_address is not set") + return Config{}, fmt.Errorf("opendtu_address is not set") } if config.OpenDTUAuth { if config.OpenDTUUser == "" { - log.Fatal("opendtu_username is not set, while opendtu_auth is set to enabled. Set opendtu_auth to false or set username") + return Config{}, fmt.Errorf("opendtu_username is not set, while opendtu_auth is set to enabled. Set opendtu_auth to false or set username") } if config.OpenDTUPassword == "" { - log.Fatal("opendtu_password is not set, while opendtu_auth is set to enabled. Set opendtu_auth to false or set password") + return Config{}, fmt.Errorf("opendtu_password is not set, while opendtu_auth is set to enabled. Set opendtu_auth to false or set password") } } } else { @@ -197,29 +212,29 @@ func loadConfig() Config { // Fallback to environment variables config.DB = os.Getenv("DB_URL") if config.DB == "" { - log.Fatal("DB_URL environment variable is not set.") + return Config{}, fmt.Errorf("DB_URL environment variable is not set") } config.OpenDTUAddress = os.Getenv("OPENDTU_ADDRESS") if config.OpenDTUAddress == "" { - log.Fatal("OPENDTU_ADDRESS environment variable is not set.") + return Config{}, fmt.Errorf("OPENDTU_ADDRESS environment variable is not set") } openDTUAuthStr := os.Getenv("OPENDTU_AUTH") if openDTUAuthStr != "" { openDTUAuth, err := strconv.ParseBool(openDTUAuthStr) if err != nil { - log.Fatalf("Error parsing OPENDTU_AUTH: %v", err) + return Config{}, fmt.Errorf("error parsing OPENDTU_AUTH: %w", err) } config.OpenDTUAuth = openDTUAuth } if config.OpenDTUAuth { config.OpenDTUUser = os.Getenv("OPENDTU_USERNAME") if config.OpenDTUUser == "" { - log.Fatal("OPENDTU_USERNAME environment variable is not set.") + return Config{}, fmt.Errorf("OPENDTU_USERNAME environment variable is not set") } config.OpenDTUPassword = os.Getenv("OPENDTU_PASSWORD") if config.OpenDTUPassword == "" { - log.Fatal("OPENDTU_PASSWORD environment variable is not set.") + return Config{}, fmt.Errorf("OPENDTU_PASSWORD environment variable is not set") } } @@ -228,7 +243,7 @@ func loadConfig() Config { if timescaleDBStr != "" { timescaleDB, err := strconv.ParseBool(timescaleDBStr) if err != nil { - log.Fatalf("Error parsing TIMESCALEDB_ENABLED: %v", err) + return Config{}, fmt.Errorf("error parsing TIMESCALEDB_ENABLED: %w", err) } config.TimescaleDB = timescaleDB } @@ -240,7 +255,7 @@ func loadConfig() Config { logger.Warn("invalid timezone") } - return config + return config, nil } // Helper function to map environment variable to slog.Level @@ -273,7 +288,11 @@ func main() { slog.SetDefault(logger) // Load the configuration - config := loadConfig() + var err error + config, err = loadConfig() + if err != nil { + log.Fatal(err) + } // Set the logLevel logLevel := getLogLevel(slog.LevelInfo) // Default to info level @@ -468,9 +487,9 @@ func insertLiveData(db *sql.DB, inverter Inverter, total Total, hints Hints) { } // Insert data into hints table _, err = db.Exec(` - INSERT INTO opendtu_hints (timestamp, time_sync, radio_problem, default_password) - VALUES ($1, $2, $3, $4); - `, timestamp, hints.TimeSync, hints.RadioProblem, hints.DefaultPassword) + INSERT INTO opendtu_hints (timestamp, time_sync, radio_problem, default_password, pin_mapping_issue) + VALUES ($1, $2, $3, $4, $5); + `, timestamp, hints.TimeSync, hints.RadioProblem, hints.DefaultPassword, hints.PinMappingIssue) if err != nil { logger.Error("Error inserting into log table", "error", err) return diff --git a/migrations/00009_hints_pin_mapping.sql b/migrations/00009_hints_pin_mapping.sql new file mode 100644 index 0000000..73f5b74 --- /dev/null +++ b/migrations/00009_hints_pin_mapping.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE opendtu_hints ADD COLUMN IF NOT EXISTS pin_mapping_issue BOOLEAN NOT NULL DEFAULT FALSE; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE opendtu_hints DROP COLUMN IF EXISTS pin_mapping_issue; +-- +goose StatementEnd