Skip to content

Commit

Permalink
add inverter realtime data with IDC and UDC data fields
Browse files Browse the repository at this point in the history
  • Loading branch information
chrikoch committed Aug 27, 2024
1 parent 10916f5 commit e7697e6
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 25 deletions.
1 change: 1 addition & 0 deletions cfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func setupCliFlags(version string, fs *flag.FlagSet, config *Configuration) {
"Timeout in seconds when collecting metrics from Fronius Symo. Should not be larger than the scrape interval.")
fs.Bool("symo.enable-power-flow", config.Symo.PowerFlowEnabled, "Enable/disable scraping of power flow data")
fs.Bool("symo.enable-archive", config.Symo.ArchiveEnabled, "Enable/disable scraping of archive data")
fs.Bool("symo.enable-inverter-realtime", config.Symo.InverterRealtimeEnabled, "Enable/disable scraping of inverter real time data")
}

func postLoadProcess(config *Configuration) {
Expand Down
22 changes: 12 additions & 10 deletions cfg/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ type (
}
// SymoConfig configures the Fronius Symo device
SymoConfig struct {
URL string `koanf:"url"`
Timeout time.Duration `koanf:"timeout"`
Headers []string `koanf:"header"`
PowerFlowEnabled bool `koanf:"enable-power-flow"`
ArchiveEnabled bool `koanf:"enable-archive"`
URL string `koanf:"url"`
Timeout time.Duration `koanf:"timeout"`
Headers []string `koanf:"header"`
PowerFlowEnabled bool `koanf:"enable-power-flow"`
ArchiveEnabled bool `koanf:"enable-archive"`
InverterRealtimeEnabled bool `koanf:"enable-inverter-realtime"`
}
)

Expand All @@ -31,11 +32,12 @@ func NewDefaultConfig() *Configuration {
Level: "info",
},
Symo: SymoConfig{
URL: "http://symo.ip.or.hostname",
Timeout: 5 * time.Second,
Headers: []string{},
PowerFlowEnabled: true,
ArchiveEnabled: true,
URL: "http://symo.ip.or.hostname",
Timeout: 5 * time.Second,
Headers: []string{},
PowerFlowEnabled: true,
ArchiveEnabled: true,
InverterRealtimeEnabled: true,
},
BindAddr: ":8080",
}
Expand Down
13 changes: 7 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@ func main() {
headers := http.Header{}
cfg.ConvertHeaders(config.Symo.Headers, &headers)
symoClient, err := fronius.NewSymoClient(fronius.ClientOptions{
URL: config.Symo.URL,
Headers: headers,
Timeout: config.Symo.Timeout,
PowerFlowEnabled: config.Symo.PowerFlowEnabled,
ArchiveEnabled: config.Symo.ArchiveEnabled,
URL: config.Symo.URL,
Headers: headers,
Timeout: config.Symo.Timeout,
PowerFlowEnabled: config.Symo.PowerFlowEnabled,
ArchiveEnabled: config.Symo.ArchiveEnabled,
InverterRealtimeEnabled: config.Symo.InverterRealtimeEnabled,
})
if err != nil {
log.WithError(err).Fatal("Cannot initialize Fronius Symo client.")
}
if !config.Symo.ArchiveEnabled && !config.Symo.PowerFlowEnabled {
if !config.Symo.ArchiveEnabled && !config.Symo.PowerFlowEnabled && !config.Symo.InverterRealtimeEnabled {
log.Fatal("All scrape endpoints are disabled. You need enable at least one endpoint.")
}

Expand Down
70 changes: 69 additions & 1 deletion metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,47 @@ var (
Name: "site_mppt_current_dc",
Help: "Site mppt current DC in A",
}, []string{"inverter", "mppt"})

siteRealtimeDataIDCGauge1 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_idc1",
Help: "Site real time data DC current string 1",
})
siteRealtimeDataIDCGauge2 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_idc2",
Help: "Site real time data DC current string 2",
})
siteRealtimeDataIDCGauge3 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_idc3",
Help: "Site real time data DC current string 3",
})
siteRealtimeDataIDCGauge4 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_idc4",
Help: "Site real time data DC current string 4",
})
siteRealtimeDataUDCGauge1 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_udc1",
Help: "Site real time data DC voltage string 1",
})
siteRealtimeDataUDCGauge2 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_udc2",
Help: "Site real time data DC voltage string 2",
})
siteRealtimeDataUDCGauge3 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_udc3",
Help: "Site real time data DC voltage string 3",
})
siteRealtimeDataUDCGauge4 = promauto.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Name: "site_realtime_data_udc4",
Help: "Site real time data DC voltage string 4",
})
)

func collectMetricsFromTarget(client *fronius.SymoClient) {
Expand All @@ -96,10 +137,11 @@ func collectMetricsFromTarget(client *fronius.SymoClient) {
}).Debug("Requesting data.")

wg := sync.WaitGroup{}
wg.Add(2)
wg.Add(3)

collectPowerFlowData(client, &wg)
collectArchiveData(client, &wg)
collectInverterRealtimeData(client, &wg)

wg.Wait()
elapsed := time.Since(start)
Expand All @@ -119,6 +161,19 @@ func collectPowerFlowData(client *fronius.SymoClient, w *sync.WaitGroup) {
}
}

func collectInverterRealtimeData(client *fronius.SymoClient, w *sync.WaitGroup) {
defer w.Done()
if client.Options.InverterRealtimeEnabled {
powerFlowData, err := client.GetInverterRealtimeData()
if err != nil {
log.WithError(err).Warn("Could not collect Symo inverter realtime metrics.")
scrapeErrorCount.Add(1)
return
}
parseInverterRealtimeData(powerFlowData)
}
}

func collectArchiveData(client *fronius.SymoClient, w *sync.WaitGroup) {
defer w.Done()
if client.Options.ArchiveEnabled {
Expand Down Expand Up @@ -155,6 +210,19 @@ func parsePowerFlowMetrics(data *fronius.SymoData) {
}
}

func parseInverterRealtimeData(data *fronius.SymoInverterRealtimeData) {
log.WithField("InverterRealtimeData", *data).Debug("Parsing data.")
siteRealtimeDataIDCGauge1.Set(data.IDC1.Value)
siteRealtimeDataIDCGauge2.Set(data.IDC2.Value)
siteRealtimeDataIDCGauge3.Set(data.IDC3.Value)
siteRealtimeDataIDCGauge4.Set(data.IDC4.Value)

siteRealtimeDataUDCGauge1.Set(data.UDC1.Value)
siteRealtimeDataUDCGauge2.Set(data.UDC2.Value)
siteRealtimeDataUDCGauge3.Set(data.UDC3.Value)
siteRealtimeDataUDCGauge4.Set(data.UDC4.Value)
}

func parseArchiveMetrics(data map[string]fronius.InverterArchive) {
log.WithField("archiveData", data).Debug("Parsing data.")
for key, inverter := range data {
Expand Down
58 changes: 53 additions & 5 deletions pkg/fronius/symo.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const (
PowerDataPath = "/solar_api/v1/GetPowerFlowRealtimeData.fcgi"
// ArchiveDataPath is the Fronius API URL-path for archive data
ArchiveDataPath = "/solar_api/v1/GetArchiveData.cgi?Scope=System&Channel=Voltage_DC_String_1&Channel=Current_DC_String_1&Channel=Voltage_DC_String_2&Channel=Current_DC_String_2&HumanReadable=false"
// InverterRealtimeDataPath is the Fronius API URL-path for inverter real time data
InverterRealtimeDataPath = "/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceId=1&DataCollection=CommonInverterData"
)

type (
Expand Down Expand Up @@ -63,6 +65,28 @@ type (
EnergyTotal float64 `json:"E_Total"`
}

symoInverterRealtime struct {
Body struct {
Data SymoInverterRealtimeData `json:"Data"`
}
}
SymoInverterRealtimeData struct {
IDC1 RealTimeDataPoint `json:"IDC"`
IDC2 RealTimeDataPoint `json:"IDC_2"`
IDC3 RealTimeDataPoint `json:"IDC_3"`
IDC4 RealTimeDataPoint `json:"IDC_4"`

UDC1 RealTimeDataPoint `json:"UDC"`
UDC2 RealTimeDataPoint `json:"UDC_2"`
UDC3 RealTimeDataPoint `json:"UDC_3"`
UDC4 RealTimeDataPoint `json:"UDC_4"`
}

RealTimeDataPoint struct {
Unit string `json:"Unit"`
Value float64 `json:"Value"`
}

// SymoArchive holds the parsed archive data from Symo API
symoArchive struct {
Body struct {
Expand Down Expand Up @@ -93,11 +117,12 @@ type (
}
// ClientOptions holds some parameters for the SymoClient.
ClientOptions struct {
URL string
Headers http.Header
Timeout time.Duration
PowerFlowEnabled bool
ArchiveEnabled bool
URL string
Headers http.Header
Timeout time.Duration
PowerFlowEnabled bool
ArchiveEnabled bool
InverterRealtimeEnabled bool
}
)

Expand Down Expand Up @@ -134,6 +159,29 @@ func (c *SymoClient) GetPowerFlowData() (*SymoData, error) {
return &p.Body.Data, nil
}

// GetPowerFlowData returns the parsed data from the Symo device.
func (c *SymoClient) GetInverterRealtimeData() (*SymoInverterRealtimeData, error) {
u, err := url.Parse(c.Options.URL + InverterRealtimeDataPath)
if err != nil {
return nil, err
}

c.request.URL = u
client := http.DefaultClient
client.Timeout = c.Options.Timeout
response, err := client.Do(c.request)
if err != nil {
return nil, err
}
defer response.Body.Close()
p := symoInverterRealtime{}
err = json.NewDecoder(response.Body).Decode(&p)
if err != nil {
return nil, err
}
return &p.Body.Data, nil
}

// GetArchiveData returns the parsed data from the Symo device.
func (c *SymoClient) GetArchiveData() (map[string]InverterArchive, error) {
u, err := url.Parse(c.Options.URL + ArchiveDataPath)
Expand Down
7 changes: 4 additions & 3 deletions pkg/fronius/symo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ func Test_Symo_GetArchiveData_GivenUrl_WhenRequestData_ThenParseStruct(t *testin
}))

c, err := NewSymoClient(ClientOptions{
URL: server.URL,
PowerFlowEnabled: true,
ArchiveEnabled: true,
URL: server.URL,
PowerFlowEnabled: true,
ArchiveEnabled: true,
InverterRealtimeEnabled: true,
})
require.NoError(t, err)

Expand Down

0 comments on commit e7697e6

Please sign in to comment.