Tariffs: wait for startup
This commit is contained in:
parent
3b438c69a4
commit
c13235a73e
6 changed files with 143 additions and 30 deletions
|
|
@ -14,6 +14,9 @@ var ErrSponsorRequired = errors.New("sponsorship required, see https://github.co
|
|||
// ErrMissingCredentials indicates that user/password are missing
|
||||
var ErrMissingCredentials = errors.New("missing credentials")
|
||||
|
||||
// ErrOutdated indicates that result is outdated
|
||||
var ErrOutdated = errors.New("outdated")
|
||||
|
||||
// ErrTimeout is the error returned when a timeout happened.
|
||||
// Modeled after context.DeadlineError
|
||||
var ErrTimeout error = errTimeoutError{}
|
||||
|
|
|
|||
74
cmd/tariff.go
Normal file
74
cmd/tariff.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/evcc-io/evcc/tariff"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// tariffCmd represents the vehicle command
|
||||
var tariffCmd = &cobra.Command{
|
||||
Use: "tariff [name]",
|
||||
Short: "Query configured tariff",
|
||||
Run: runTariff,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(tariffCmd)
|
||||
tariffCmd.PersistentFlags().StringP(flagName, "n", "", fmt.Sprintf(flagNameDescription, "vehicle"))
|
||||
}
|
||||
|
||||
func runTariff(cmd *cobra.Command, args []string) {
|
||||
// load config
|
||||
if err := loadConfigFile(&conf); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
// setup environment
|
||||
if err := configureEnvironment(cmd, conf); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
name := cmd.Flags().Lookup(flagName).Value.String()
|
||||
|
||||
for key, cc := range map[string]typedConfig{
|
||||
"grid": conf.Tariffs.Grid,
|
||||
"feedin": conf.Tariffs.FeedIn,
|
||||
"planner": conf.Tariffs.Planner,
|
||||
} {
|
||||
if cc.Type == "" || (name != "" && key != name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
fmt.Println(key + ":")
|
||||
}
|
||||
|
||||
tf, err := tariff.NewFromConfig(cc.Type, cc.Other)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
rates, err := tf.Rates()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
||||
fmt.Fprintln(tw, "From\tTo\tPrice/Cost")
|
||||
const format = "2006-01-02 15:04:05"
|
||||
|
||||
for _, r := range rates {
|
||||
fmt.Fprintf(tw, "%s\t%s\t%.3f\n", r.Start.Local().Format(format), r.End.Local().Format(format), r.Price)
|
||||
}
|
||||
tw.Flush()
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// wait for shutdown
|
||||
<-shutdownDoneC()
|
||||
}
|
||||
|
|
@ -13,10 +13,11 @@ import (
|
|||
)
|
||||
|
||||
type Awattar struct {
|
||||
mux sync.Mutex
|
||||
log *util.Logger
|
||||
uri string
|
||||
data api.Rates
|
||||
mux sync.Mutex
|
||||
log *util.Logger
|
||||
uri string
|
||||
data api.Rates
|
||||
updated time.Time
|
||||
}
|
||||
|
||||
var _ api.Tariff = (*Awattar)(nil)
|
||||
|
|
@ -47,12 +48,15 @@ func NewAwattarFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
t.log.WARN.Println("cheap rate configuration has been replaced by target charging and is deprecated")
|
||||
}
|
||||
|
||||
go t.Run()
|
||||
done := make(chan error)
|
||||
go t.run(done)
|
||||
err := <-done
|
||||
|
||||
return t, nil
|
||||
return t, err
|
||||
}
|
||||
|
||||
func (t *Awattar) Run() {
|
||||
func (t *Awattar) run(done chan error) {
|
||||
var once sync.Once
|
||||
client := request.NewHelper(t.log)
|
||||
|
||||
for ; true; <-time.NewTicker(time.Hour).C {
|
||||
|
|
@ -62,9 +66,12 @@ func (t *Awattar) Run() {
|
|||
continue
|
||||
}
|
||||
|
||||
t.mux.Lock()
|
||||
t.data = make(api.Rates, 0, len(res.Data))
|
||||
once.Do(func() { close(done) })
|
||||
|
||||
t.mux.Lock()
|
||||
t.updated = time.Now()
|
||||
|
||||
t.data = make(api.Rates, 0, len(res.Data))
|
||||
for _, r := range res.Data {
|
||||
ar := api.Rate{
|
||||
Start: r.StartTimestamp,
|
||||
|
|
@ -82,5 +89,5 @@ func (t *Awattar) Run() {
|
|||
func (t *Awattar) Rates() (api.Rates, error) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
return append([]api.Rate{}, t.data...), nil
|
||||
return append([]api.Rate{}, t.data...), outdatedError(t.updated, time.Hour)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@ import (
|
|||
|
||||
type ElectricityMaps struct {
|
||||
*request.Helper
|
||||
log *util.Logger
|
||||
mux sync.Mutex
|
||||
uri string
|
||||
zone string
|
||||
data []CarbonIntensitySlot
|
||||
log *util.Logger
|
||||
mux sync.Mutex
|
||||
uri string
|
||||
zone string
|
||||
data []CarbonIntensitySlot
|
||||
updated time.Time
|
||||
}
|
||||
|
||||
type CarbonIntensity struct {
|
||||
|
|
@ -29,8 +30,8 @@ type CarbonIntensity struct {
|
|||
}
|
||||
|
||||
type CarbonIntensitySlot struct {
|
||||
CarbonIntensity float64 // : 626,
|
||||
Datetime time.Time // : "2022-12-12T16:00:00.000Z"
|
||||
CarbonIntensity float64 // 626,
|
||||
Datetime time.Time // "2022-12-12T16:00:00.000Z"
|
||||
}
|
||||
|
||||
var _ api.Tariff = (*ElectricityMaps)(nil)
|
||||
|
|
@ -68,12 +69,15 @@ func NewElectricityMapsFromConfig(other map[string]interface{}) (api.Tariff, err
|
|||
}),
|
||||
}
|
||||
|
||||
go t.Run()
|
||||
done := make(chan error)
|
||||
go t.run(done)
|
||||
err := <-done
|
||||
|
||||
return t, nil
|
||||
return t, err
|
||||
}
|
||||
|
||||
func (t *ElectricityMaps) Run() {
|
||||
func (t *ElectricityMaps) run(done chan error) {
|
||||
var once sync.Once
|
||||
uri := fmt.Sprintf("%s/carbon-intensity/forecast?zone=%s", t.uri, t.zone)
|
||||
|
||||
for ; true; <-time.NewTicker(time.Hour).C {
|
||||
|
|
@ -83,11 +87,16 @@ func (t *ElectricityMaps) Run() {
|
|||
err = errors.New(res.Error)
|
||||
}
|
||||
|
||||
once.Do(func() { done <- err })
|
||||
|
||||
t.log.ERROR.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
once.Do(func() { close(done) })
|
||||
|
||||
t.mux.Lock()
|
||||
t.updated = time.Now()
|
||||
t.data = res.Forecast
|
||||
t.mux.Unlock()
|
||||
}
|
||||
|
|
@ -107,5 +116,5 @@ func (t *ElectricityMaps) Rates() (api.Rates, error) {
|
|||
res = append(res, ar)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return res, outdatedError(t.updated, time.Hour)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package tariff
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"golang.org/x/text/currency"
|
||||
)
|
||||
|
|
@ -22,3 +24,11 @@ func NewTariffs(currency currency.Unit, grid, feedin, planner api.Tariff) *Tarif
|
|||
Planner: planner,
|
||||
}
|
||||
}
|
||||
|
||||
// outdatedError returns api.ErrOutdated if t is older than 2*d
|
||||
func outdatedError(t time.Time, d time.Duration) error {
|
||||
if time.Since(t) > 2*d {
|
||||
return api.ErrOutdated
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ import (
|
|||
)
|
||||
|
||||
type Tibber struct {
|
||||
mux sync.Mutex
|
||||
log *util.Logger
|
||||
homeID string
|
||||
client *tibber.Client
|
||||
data api.Rates
|
||||
mux sync.Mutex
|
||||
log *util.Logger
|
||||
homeID string
|
||||
client *tibber.Client
|
||||
data api.Rates
|
||||
updated time.Time
|
||||
}
|
||||
|
||||
var _ api.Tariff = (*Tibber)(nil)
|
||||
|
|
@ -62,12 +63,16 @@ func NewTibberFromConfig(other map[string]interface{}) (api.Tariff, error) {
|
|||
t.log.WARN.Println("cheap rate configuration has been replaced by target charging and is deprecated")
|
||||
}
|
||||
|
||||
go t.Run()
|
||||
done := make(chan error)
|
||||
go t.run(done)
|
||||
err := <-done
|
||||
|
||||
return t, nil
|
||||
return t, err
|
||||
}
|
||||
|
||||
func (t *Tibber) Run() {
|
||||
func (t *Tibber) run(done chan error) {
|
||||
var once sync.Once
|
||||
|
||||
for ; true; <-time.NewTicker(time.Hour).C {
|
||||
var res struct {
|
||||
Viewer struct {
|
||||
|
|
@ -88,11 +93,16 @@ func (t *Tibber) Run() {
|
|||
cancel()
|
||||
|
||||
if err != nil {
|
||||
once.Do(func() { done <- err })
|
||||
|
||||
t.log.ERROR.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
once.Do(func() { close(done) })
|
||||
|
||||
t.mux.Lock()
|
||||
t.updated = time.Now()
|
||||
|
||||
pi := res.Viewer.Home.CurrentSubscription.PriceInfo
|
||||
t.data = make(api.Rates, 0, len(pi.Today)+len(pi.Tomorrow))
|
||||
|
|
@ -119,5 +129,5 @@ func (t *Tibber) rates(pi []tibber.PriceInfo) api.Rates {
|
|||
func (t *Tibber) Rates() (api.Rates, error) {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
return append([]api.Rate{}, t.data...), nil
|
||||
return append([]api.Rate{}, t.data...), outdatedError(t.updated, time.Hour)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue