Expand hints. Add RadioStatistics struct. Add new Order, DataAge and DataAgeMs.

This commit is contained in:
Pieter Hollander 2025-10-04 01:03:49 +02:00
parent 163e3672ef
commit 851c6d01ec
Signed by: pieter
SSH key fingerprint: SHA256:HbX+9cBXsop9SuvL+mELd29sK+7DehFfdVweFVDtMSg
2 changed files with 49 additions and 21 deletions

61
main.go
View file

@ -72,20 +72,34 @@ type InverterINV struct {
YieldTotal VUD `json:"YieldTotal"` 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 // Inverter struct
type Inverter struct { type Inverter struct {
Serial string `json:"serial"` Serial string `json:"serial"`
Name string `json:"name"` Name string `json:"name"`
Order int `json:"order"`
DataAge int `json:"data_age"`
DataAgeMs int `json:"data_age_ms"`
Producing bool `json:"producing"` Producing bool `json:"producing"`
LimitRelative float64 `json:"limit_relative"` LimitRelative float64 `json:"limit_relative"`
LimitAbsolute float64 `json:"limit_absolute"` 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"` PollEnabled bool `json:"poll_enabled"`
Reachable bool `json:"reachable"` 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"` INV map[string]InverterINV `json:"INV"`
RadioStats RadioStatistics `json:"radio_stats"`
} }
type Total struct { type Total struct {
@ -98,6 +112,7 @@ type Hints struct {
TimeSync bool `json:"time_sync"` TimeSync bool `json:"time_sync"`
RadioProblem bool `json:"radio_problem"` RadioProblem bool `json:"radio_problem"`
DefaultPassword bool `json:"default_password"` DefaultPassword bool `json:"default_password"`
PinMappingIssue bool `json:"pin_mapping_issue"`
} }
type LiveData struct { type LiveData struct {
@ -165,7 +180,7 @@ var config Config
// LoadConfig attempts to read the configuration from options.json // LoadConfig attempts to read the configuration from options.json
// If it fails, it falls back to using environment variables // If it fails, it falls back to using environment variables
func loadConfig() Config { func loadConfig() (Config, error) {
configFilePath := os.Getenv("CONFIG_FILE") configFilePath := os.Getenv("CONFIG_FILE")
if configFilePath == "" { if configFilePath == "" {
configFilePath = "/data/options.json" configFilePath = "/data/options.json"
@ -176,20 +191,20 @@ func loadConfig() Config {
// Successfully read the file, parse the JSON // Successfully read the file, parse the JSON
err = json.Unmarshal(data, &config) err = json.Unmarshal(data, &config)
if err != nil { if err != nil {
log.Fatalf("Error parsing config file: %v", err) return Config{}, fmt.Errorf("error parsing config file: %w", err)
} }
if config.DB == "" { if config.DB == "" {
log.Fatal("db connection settings are not set") return Config{}, fmt.Errorf("db connection settings are not set")
} }
if config.OpenDTUAddress == "" { if config.OpenDTUAddress == "" {
log.Fatal("opendtu_address is not set") return Config{}, fmt.Errorf("opendtu_address is not set")
} }
if config.OpenDTUAuth { if config.OpenDTUAuth {
if config.OpenDTUUser == "" { 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 == "" { 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 { } else {
@ -197,29 +212,29 @@ func loadConfig() Config {
// Fallback to environment variables // Fallback to environment variables
config.DB = os.Getenv("DB_URL") config.DB = os.Getenv("DB_URL")
if config.DB == "" { 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") config.OpenDTUAddress = os.Getenv("OPENDTU_ADDRESS")
if config.OpenDTUAddress == "" { 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") openDTUAuthStr := os.Getenv("OPENDTU_AUTH")
if openDTUAuthStr != "" { if openDTUAuthStr != "" {
openDTUAuth, err := strconv.ParseBool(openDTUAuthStr) openDTUAuth, err := strconv.ParseBool(openDTUAuthStr)
if err != nil { if err != nil {
log.Fatalf("Error parsing OPENDTU_AUTH: %v", err) return Config{}, fmt.Errorf("error parsing OPENDTU_AUTH: %w", err)
} }
config.OpenDTUAuth = openDTUAuth config.OpenDTUAuth = openDTUAuth
} }
if config.OpenDTUAuth { if config.OpenDTUAuth {
config.OpenDTUUser = os.Getenv("OPENDTU_USERNAME") config.OpenDTUUser = os.Getenv("OPENDTU_USERNAME")
if config.OpenDTUUser == "" { 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") config.OpenDTUPassword = os.Getenv("OPENDTU_PASSWORD")
if config.OpenDTUPassword == "" { 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 != "" { if timescaleDBStr != "" {
timescaleDB, err := strconv.ParseBool(timescaleDBStr) timescaleDB, err := strconv.ParseBool(timescaleDBStr)
if err != nil { if err != nil {
log.Fatalf("Error parsing TIMESCALEDB_ENABLED: %v", err) return Config{}, fmt.Errorf("error parsing TIMESCALEDB_ENABLED: %w", err)
} }
config.TimescaleDB = timescaleDB config.TimescaleDB = timescaleDB
} }
@ -240,7 +255,7 @@ func loadConfig() Config {
logger.Warn("invalid timezone") logger.Warn("invalid timezone")
} }
return config return config, nil
} }
// Helper function to map environment variable to slog.Level // Helper function to map environment variable to slog.Level
@ -273,7 +288,11 @@ func main() {
slog.SetDefault(logger) slog.SetDefault(logger)
// Load the configuration // Load the configuration
config := loadConfig() var err error
config, err = loadConfig()
if err != nil {
log.Fatal(err)
}
// Set the logLevel // Set the logLevel
logLevel := getLogLevel(slog.LevelInfo) // Default to info level 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 // Insert data into hints table
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO opendtu_hints (timestamp, time_sync, radio_problem, default_password) INSERT INTO opendtu_hints (timestamp, time_sync, radio_problem, default_password, pin_mapping_issue)
VALUES ($1, $2, $3, $4); VALUES ($1, $2, $3, $4, $5);
`, timestamp, hints.TimeSync, hints.RadioProblem, hints.DefaultPassword) `, timestamp, hints.TimeSync, hints.RadioProblem, hints.DefaultPassword, hints.PinMappingIssue)
if err != nil { if err != nil {
logger.Error("Error inserting into log table", "error", err) logger.Error("Error inserting into log table", "error", err)
return return

View file

@ -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