evcc-io/util/config/config.go

158 lines
3.7 KiB
Go

package config
import (
"fmt"
"github.com/evcc-io/evcc/util/templates"
"gorm.io/gorm"
)
type Config struct {
ID int `gorm:"primarykey"`
Class templates.Class
Type string
Details []ConfigDetail `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}
type ConfigDetail struct {
ConfigID int `gorm:"index:idx_unique"`
Key string `gorm:"index:idx_unique"`
Value string
}
// Named converts device details to named config
func (d *Config) Named() Named {
res := Named{
Name: NameForID(d.ID),
Type: d.Type,
Other: d.detailsAsMap(),
}
return res
}
// Typed converts device details to typed config
func (d *Config) Typed() Typed {
res := Typed{
Type: d.Type,
Other: d.detailsAsMap(),
}
return res
}
// detailsAsMap converts device details to map
func (d *Config) detailsAsMap() map[string]any {
res := make(map[string]any, len(d.Details))
for _, detail := range d.Details {
res[detail.Key] = detail.Value
}
return res
}
// detailsFromMap converts map to device details
func detailsFromMap(config map[string]any) []ConfigDetail {
res := make([]ConfigDetail, 0, len(config))
for k, v := range config {
res = append(res, ConfigDetail{Key: k, Value: fmt.Sprintf("%v", v)})
}
return res
}
// Update updates a config's details to the database
func (d *Config) Update(conf map[string]any) error {
return db.Transaction(func(tx *gorm.DB) error {
var config Config
if err := tx.Where(Config{Class: d.Class, ID: d.ID}).First(&config).Error; err != nil {
return err
}
if err := tx.Delete(new(ConfigDetail), ConfigDetail{ConfigID: d.ID}).Error; err != nil {
return err
}
d.Details = detailsFromMap(conf)
return tx.Save(&d).Error
})
}
// Delete deletes a config from the database
func (d *Config) Delete() error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Delete(new(ConfigDetail), ConfigDetail{ConfigID: d.ID}).Error; err != nil {
return err
}
return tx.Delete(Config{ID: d.ID}).Error
})
}
var db *gorm.DB
func Init(instance *gorm.DB) error {
db = instance
for old, new := range map[string]string{
"devices": "configs",
"device_details": "config_details",
} {
if m := db.Migrator(); m.HasTable(old) {
if err := m.RenameTable(old, new); err != nil {
return err
}
}
}
err := db.AutoMigrate(new(Config), new(ConfigDetail))
if err == nil && db.Migrator().HasConstraint(new(ConfigDetail), "fk_devices_details") {
err = db.Migrator().DropConstraint(new(ConfigDetail), "fk_devices_details")
}
if err == nil && db.Migrator().HasColumn(new(ConfigDetail), "device_id") {
err = db.Migrator().DropColumn(new(ConfigDetail), "device_id")
}
return err
}
// NameForID returns a unique config name for the given id
func NameForID(id int) string {
return fmt.Sprintf("db:%d", id)
}
// ConfigurationsByClass returns devices by class from the database
func ConfigurationsByClass(class templates.Class) ([]Config, error) {
var devices []Config
tx := db.Where(&Config{Class: class}).Preload("Details").Order("id").Find(&devices)
// remove devices without details
res := make([]Config, 0, len(devices))
for _, dev := range devices {
if len(dev.Details) > 0 {
res = append(res, dev)
}
}
return res, tx.Error
}
// ConfigByID returns device by id from the database
func ConfigByID(id int) (Config, error) {
var config Config
tx := db.Where(&Config{ID: id}).Preload("Details").First(&config)
return config, tx.Error
}
// AddConfig adds a new config to the database
func AddConfig(class templates.Class, typ string, conf map[string]any) (Config, error) {
config := Config{
Class: class,
Type: typ,
Details: detailsFromMap(conf),
}
err := db.Transaction(func(tx *gorm.DB) error {
return tx.Create(&config).Error
})
return config, err
}