Tariffs: add formulas (#17002)
This commit is contained in:
parent
c9b40dba69
commit
bb8bf49f8d
13 changed files with 114 additions and 3 deletions
|
|
@ -197,6 +197,13 @@ tariffs:
|
|||
# uri: https://example.org/price.json
|
||||
# jq: .price.current
|
||||
|
||||
# type: template
|
||||
# template: energy-charts-api # epex spot market prices
|
||||
# bzn: DE-LU
|
||||
# charges: 0.15
|
||||
# tax: 0.1
|
||||
# formula: math.Min((price + charges) * (1 + tax), 0.5)
|
||||
|
||||
feedin:
|
||||
# rate for feeding excess (pv) energy to the grid
|
||||
type: fixed
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ func NewAwattarFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &Awattar{
|
||||
embed: &cc.embed,
|
||||
log: util.NewLogger("awattar"),
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ func NewEdfTempoFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
return nil, errors.New("missing credentials")
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
basic := transport.BasicAuthHeader(cc.ClientID, cc.ClientSecret)
|
||||
log := util.NewLogger("edf-tempo").Redact(basic)
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ func NewEleringFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
return nil, errors.New("missing region")
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &Elering{
|
||||
embed: &cc.embed,
|
||||
log: util.NewLogger("elering"),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,71 @@
|
|||
package tariff
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/traefik/yaegi/interp"
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
type embed struct {
|
||||
Margin float64 `mapstructure:"margin"`
|
||||
Charges float64 `mapstructure:"charges"`
|
||||
Tax float64 `mapstructure:"tax"`
|
||||
Uplifts float64 `mapstructure:"uplifts"`
|
||||
Formula string `mapstructure:"formula"`
|
||||
|
||||
calc func(float64) (float64, error)
|
||||
}
|
||||
|
||||
func (t *embed) init() error {
|
||||
if t.Formula == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
vm := interp.New(interp.Options{})
|
||||
if err := vm.Use(stdlib.Symbols); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := vm.Eval(`import "math"`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := vm.Eval("var price, charges, tax float64"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prg, err := vm.Compile(t.Formula)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.calc = func(price float64) (float64, error) {
|
||||
if _, err := vm.Eval(fmt.Sprintf("price = %f", price)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
res, err := vm.Execute(prg)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !res.CanFloat() {
|
||||
return 0, errors.New("formula did not return a float value")
|
||||
}
|
||||
|
||||
return res.Float(), nil
|
||||
}
|
||||
|
||||
// test the formula
|
||||
_, err = t.calc(0)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *embed) totalPrice(price float64) float64 {
|
||||
return (price*(1+t.Margin)+t.Charges)*(1+t.Tax) + t.Uplifts
|
||||
if t.calc != nil {
|
||||
res, _ := t.calc(price)
|
||||
return res
|
||||
}
|
||||
return (price + t.Charges) * (1 + t.Tax)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ func NewEnerginetFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
return nil, errors.New("missing region")
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &Energinet{
|
||||
embed: &cc.embed,
|
||||
log: util.NewLogger("energinet"),
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ func NewEntsoeFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
return nil, errors.New("missing domain")
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domain, err := entsoe.Area(entsoe.BZN, strings.ToUpper(cc.Domain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ func NewPunFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &Pun{
|
||||
log: util.NewLogger("pun"),
|
||||
embed: &cc,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ func NewSmartEnergyFromConfig(other map[string]interface{}) (api.Tariff, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &SmartEnergy{
|
||||
embed: &cc.embed,
|
||||
log: util.NewLogger("smartenergy"),
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ func NewConfigurableFromConfig(ctx context.Context, other map[string]interface{}
|
|||
return nil, fmt.Errorf("must have either price or forecast")
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
priceG func() (float64, error)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ func NewTibberFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
return nil, errors.New("missing token")
|
||||
}
|
||||
|
||||
if err := cc.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := util.NewLogger("tibber").Redact(cc.Token, cc.HomeID)
|
||||
|
||||
t := &Tibber{
|
||||
|
|
|
|||
|
|
@ -333,6 +333,12 @@ presets:
|
|||
help:
|
||||
de: Zusätzlicher prozentualer Aufschlag (z.B. 0.2 für 20%)
|
||||
en: Additional percentage charge (e.g. 0.2 for 20%)
|
||||
- name: formula
|
||||
type: string
|
||||
help:
|
||||
de: Individuelle Formel zur Berechnung des Preises
|
||||
en: Individual formula for calculating the price
|
||||
example: "math.Max((price + charges) * (1 + tax), 0.0)"
|
||||
eebus:
|
||||
params:
|
||||
- name: ski
|
||||
|
|
|
|||
|
|
@ -5,4 +5,7 @@ charges: {{ .charges }}
|
|||
{{- if .tax }}
|
||||
tax: {{ .tax }}
|
||||
{{- end }}
|
||||
{{- if .formula }}
|
||||
formula: {{ .formula }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue