Tibber Pulse: detect home id
This commit is contained in:
parent
b50f0d4ac0
commit
f9eeeb50e2
5 changed files with 141 additions and 107 deletions
|
|
@ -34,52 +34,54 @@ func NewTibberFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
}
|
||||
|
||||
t := &Tibber{
|
||||
log: util.NewLogger("pulse").Redact(cc.HomeID, cc.Token),
|
||||
log: util.NewLogger("pulse").Redact(cc.Token, cc.HomeID),
|
||||
}
|
||||
|
||||
// TODO
|
||||
// https://developer.tibber.com/docs/overview#breaking-websocket-change
|
||||
// https://github.com/hasura/go-graphql-client/issues/38
|
||||
if cc.HomeID == "" {
|
||||
// query client
|
||||
qclient := tibber.NewClient(t.log, cc.Token)
|
||||
|
||||
// ctx := context.WithValue(
|
||||
// context.Background(),
|
||||
// oauth2.HTTPClient,
|
||||
// request.NewHelper(t.log).Client,
|
||||
// )
|
||||
|
||||
// client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{
|
||||
// AccessToken: t.Token,
|
||||
// }))
|
||||
|
||||
// urlClient := graphql.NewClient(tibber.URI, client)
|
||||
var err error
|
||||
if cc.HomeID, err = qclient.DefaultHomeID(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// // get websocket uri
|
||||
// var res struct {
|
||||
// Viewer struct {
|
||||
// WebsocketSubscriptionUrl string
|
||||
// }
|
||||
// }
|
||||
|
||||
// if err := urlClient.Query(context.Background(), &res, nil); err != nil {
|
||||
// panic(err)
|
||||
// ctx, cancel := context.WithTimeout(context.Background(), request.Timeout)
|
||||
// defer cancel()
|
||||
|
||||
// if err := qclient.Query(ctx, &res, nil); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// println(res.Viewer.WebsocketSubscriptionUrl)
|
||||
|
||||
// subscription client
|
||||
client := graphql.NewSubscriptionClient(tibber.SubscriptionURI).
|
||||
WithConnectionParams(map[string]any{
|
||||
"token": cc.Token,
|
||||
}).
|
||||
WithLog(t.log.TRACE.Println)
|
||||
|
||||
// Run the client
|
||||
// run the client
|
||||
go func() {
|
||||
if err := client.Run(); err != nil {
|
||||
panic(err)
|
||||
t.log.ERROR.Println(err)
|
||||
}
|
||||
println("running")
|
||||
}()
|
||||
|
||||
// subscribe
|
||||
err := t.subscribe(client, cc.HomeID)
|
||||
|
||||
return t, err
|
||||
}
|
||||
|
||||
// subscribe to the websocket query
|
||||
func (t *Tibber) subscribe(client *graphql.SubscriptionClient, homeID string) error {
|
||||
var query struct {
|
||||
tibber.LiveMeasurement `graphql:"liveMeasurement(homeId: $homeId)"`
|
||||
}
|
||||
|
|
@ -89,7 +91,7 @@ func NewTibberFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
errC := make(chan error)
|
||||
|
||||
_, err := client.Subscribe(&query, map[string]any{
|
||||
"homeId": graphql.ID(cc.HomeID),
|
||||
"homeId": graphql.ID(homeID),
|
||||
}, func(data []byte, err error) error {
|
||||
if err != nil {
|
||||
select {
|
||||
|
|
@ -130,7 +132,7 @@ func NewTibberFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return t, err
|
||||
return err
|
||||
}
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
|
|
|
|||
52
meter/tibber/client.go
Normal file
52
meter/tibber/client.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package tibber
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/shurcooL/graphql"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*graphql.Client
|
||||
}
|
||||
|
||||
func NewClient(log *util.Logger, token string) *Client {
|
||||
ctx := context.WithValue(
|
||||
context.Background(),
|
||||
oauth2.HTTPClient,
|
||||
request.NewHelper(log).Client,
|
||||
)
|
||||
|
||||
client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{
|
||||
AccessToken: token,
|
||||
}))
|
||||
|
||||
return &Client{
|
||||
Client: graphql.NewClient(URI, client),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DefaultHomeID() (string, error) {
|
||||
var res struct {
|
||||
Viewer struct {
|
||||
Homes []Home
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), request.Timeout)
|
||||
defer cancel()
|
||||
|
||||
if err := c.Query(ctx, &res, nil); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(res.Viewer.Homes) != 1 {
|
||||
return "", fmt.Errorf("could not determine home id: %v", res.Viewer.Homes)
|
||||
}
|
||||
|
||||
return res.Viewer.Homes[0].ID, nil
|
||||
}
|
||||
|
|
@ -1,6 +1,41 @@
|
|||
package tibber
|
||||
|
||||
const SubscriptionURI = "wss://api.tibber.com/v1-beta/gql/subscriptions"
|
||||
import "time"
|
||||
|
||||
const (
|
||||
URI = "https://api.tibber.com/v1-beta/gql"
|
||||
SubscriptionURI = "wss://api.tibber.com/v1-beta/gql/subscriptions"
|
||||
)
|
||||
|
||||
type Home struct {
|
||||
ID string
|
||||
TimeZone string
|
||||
Address Address
|
||||
MeteringPointData struct {
|
||||
GridCompany string
|
||||
}
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
Address1, PostalCode, City, Country string
|
||||
}
|
||||
|
||||
type Subscription struct {
|
||||
ID string
|
||||
Status string
|
||||
PriceInfo struct {
|
||||
Current PriceInfo
|
||||
Today []PriceInfo
|
||||
// Tomorrow []PriceInfo
|
||||
}
|
||||
}
|
||||
|
||||
type PriceInfo struct {
|
||||
Level string
|
||||
StartsAt time.Time
|
||||
Total float64
|
||||
// Energy, Tax float64
|
||||
}
|
||||
|
||||
type LiveMeasurement struct {
|
||||
// Timestamp time.Time
|
||||
|
|
|
|||
|
|
@ -3,54 +3,50 @@ package tariff
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/tariff/tibber"
|
||||
"github.com/evcc-io/evcc/meter/tibber"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/shurcooL/graphql"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type Tibber struct {
|
||||
mux sync.Mutex
|
||||
log *util.Logger
|
||||
Token string
|
||||
HomeID string
|
||||
Cheap float64
|
||||
client *graphql.Client
|
||||
homeID string
|
||||
cheap float64
|
||||
client *tibber.Client
|
||||
data []tibber.PriceInfo
|
||||
}
|
||||
|
||||
var _ api.Tariff = (*Tibber)(nil)
|
||||
|
||||
func NewTibber(other map[string]interface{}) (*Tibber, error) {
|
||||
t := &Tibber{}
|
||||
var cc struct {
|
||||
Token string
|
||||
HomeID string
|
||||
Cheap float64
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &t); err != nil {
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.log = util.NewLogger("tibber").Redact(t.HomeID, t.Token)
|
||||
log := util.NewLogger("tibber").Redact(cc.Token, cc.HomeID)
|
||||
|
||||
ctx := context.WithValue(
|
||||
context.Background(),
|
||||
oauth2.HTTPClient,
|
||||
request.NewHelper(t.log).Client,
|
||||
)
|
||||
t := &Tibber{
|
||||
log: log,
|
||||
homeID: cc.HomeID,
|
||||
cheap: cc.Cheap,
|
||||
client: tibber.NewClient(log, cc.Token),
|
||||
}
|
||||
|
||||
client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{
|
||||
AccessToken: t.Token,
|
||||
}))
|
||||
|
||||
t.client = graphql.NewClient(tibber.URI, client)
|
||||
|
||||
if t.HomeID == "" {
|
||||
if t.homeID == "" {
|
||||
var err error
|
||||
if t.HomeID, err = t.DefaultHomeID(); err != nil {
|
||||
if t.homeID, err = t.client.DefaultHomeID(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -60,24 +56,6 @@ func NewTibber(other map[string]interface{}) (*Tibber, error) {
|
|||
return t, nil
|
||||
}
|
||||
|
||||
func (t *Tibber) DefaultHomeID() (string, error) {
|
||||
var res struct {
|
||||
Viewer struct {
|
||||
Homes []tibber.Home
|
||||
}
|
||||
}
|
||||
|
||||
if err := t.client.Query(context.Background(), &res, nil); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(res.Viewer.Homes) != 1 {
|
||||
return "", fmt.Errorf("could not determine home id: %v", res.Viewer.Homes)
|
||||
}
|
||||
|
||||
return res.Viewer.Homes[0].ID, nil
|
||||
}
|
||||
|
||||
func (t *Tibber) Run() {
|
||||
for ; true; <-time.NewTicker(time.Hour).C {
|
||||
var res struct {
|
||||
|
|
@ -91,10 +69,14 @@ func (t *Tibber) Run() {
|
|||
}
|
||||
|
||||
v := map[string]interface{}{
|
||||
"id": graphql.ID(t.HomeID),
|
||||
"id": graphql.ID(t.homeID),
|
||||
}
|
||||
|
||||
if err := t.client.Query(context.Background(), &res, v); err != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), request.Timeout)
|
||||
err := t.client.Query(ctx, &res, v)
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
t.log.ERROR.Println(err)
|
||||
continue
|
||||
}
|
||||
|
|
@ -121,5 +103,5 @@ func (t *Tibber) CurrentPrice() (float64, error) {
|
|||
|
||||
func (t *Tibber) IsCheap() (bool, error) {
|
||||
price, err := t.CurrentPrice()
|
||||
return price <= t.Cheap, err
|
||||
return price <= t.cheap, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
package tibber
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const URI = "https://api.tibber.com/v1-beta/gql"
|
||||
|
||||
type Home struct {
|
||||
ID string
|
||||
TimeZone string
|
||||
Address Address
|
||||
MeteringPointData struct {
|
||||
GridCompany string
|
||||
}
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
Address1, PostalCode, City, Country string
|
||||
}
|
||||
|
||||
type Subscription struct {
|
||||
ID string
|
||||
Status string
|
||||
PriceInfo struct {
|
||||
Current PriceInfo
|
||||
Today []PriceInfo
|
||||
// Tomorrow []PriceInfo
|
||||
}
|
||||
}
|
||||
|
||||
type PriceInfo struct {
|
||||
Level string
|
||||
StartsAt time.Time
|
||||
Total float64
|
||||
// Energy, Tax float64
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue