This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// Package collector maps Shelly product types to Prometheus collectors.
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"lostak.dev/shelly-exporter/configuration"
|
||||
"lostak.dev/shelly-exporter/shelly"
|
||||
)
|
||||
|
||||
// Factory constructs a collector for one configured device.
|
||||
type Factory func(configuration.DeviceConfiguration, shelly.StatusClient) (prometheus.Collector, error)
|
||||
|
||||
var (
|
||||
factoriesMutex sync.RWMutex
|
||||
factories = make(map[string]Factory)
|
||||
)
|
||||
|
||||
// RegisterProduct adds support for a product identifier. Product packages call it from init.
|
||||
func RegisterProduct(product string, factory Factory) error {
|
||||
product = normalizeProduct(product)
|
||||
if product == "" {
|
||||
return fmt.Errorf("product identifier cannot be empty")
|
||||
}
|
||||
if factory == nil {
|
||||
return fmt.Errorf("factory for product %q cannot be nil", product)
|
||||
}
|
||||
|
||||
factoriesMutex.Lock()
|
||||
defer factoriesMutex.Unlock()
|
||||
if _, exists := factories[product]; exists {
|
||||
return fmt.Errorf("product %q is already registered", product)
|
||||
}
|
||||
factories[product] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDeviceCollector creates the registered collector for a configured product.
|
||||
func NewDeviceCollector(config configuration.DeviceConfiguration, client shelly.StatusClient) (prometheus.Collector, error) {
|
||||
product := normalizeProduct(config.Product)
|
||||
|
||||
factoriesMutex.RLock()
|
||||
factory, exists := factories[product]
|
||||
factoriesMutex.RUnlock()
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unsupported Shelly product %q (supported: %s)", config.Product, strings.Join(SupportedProducts(), ", "))
|
||||
}
|
||||
config.Product = product
|
||||
return factory(config, client)
|
||||
}
|
||||
|
||||
// SupportedProducts returns all registered identifiers in stable order.
|
||||
func SupportedProducts() []string {
|
||||
factoriesMutex.RLock()
|
||||
defer factoriesMutex.RUnlock()
|
||||
|
||||
products := make([]string, 0, len(factories))
|
||||
for product := range factories {
|
||||
products = append(products, product)
|
||||
}
|
||||
sort.Strings(products)
|
||||
return products
|
||||
}
|
||||
|
||||
func normalizeProduct(product string) string {
|
||||
var normalized strings.Builder
|
||||
separator := false
|
||||
for _, character := range strings.ToLower(strings.TrimSpace(product)) {
|
||||
if unicode.IsLetter(character) || unicode.IsDigit(character) {
|
||||
if separator && normalized.Len() > 0 {
|
||||
normalized.WriteByte('_')
|
||||
}
|
||||
normalized.WriteRune(character)
|
||||
separator = false
|
||||
} else {
|
||||
separator = true
|
||||
}
|
||||
}
|
||||
return normalized.String()
|
||||
}
|
||||
Reference in New Issue
Block a user