Tibber Pulse: detect home id

This commit is contained in:
andig 2022-11-20 15:35:10 +01:00
parent b50f0d4ac0
commit f9eeeb50e2
5 changed files with 141 additions and 107 deletions

View file

@ -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
View 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
}

View file

@ -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

View file

@ -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
}

View file

@ -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
}