Skip to content

Commit

Permalink
Homewizard: add cache (evcc-io#9158)
Browse files Browse the repository at this point in the history
  • Loading branch information
thierolm authored Jul 30, 2023
1 parent 4cc88a0 commit 0c9dbe0
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 52 deletions.
45 changes: 12 additions & 33 deletions charger/homewizard.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package charger

import (
"errors"
"fmt"
"net/http"
"time"

"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/meter/homewizard"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
)

// HomeWizard project homepage
Expand All @@ -26,22 +24,25 @@ func init() {

// NewHomeWizardFromConfig creates a HomeWizard charger from generic config
func NewHomeWizardFromConfig(other map[string]interface{}) (api.Charger, error) {
var cc = struct {
cc := struct {
embed `mapstructure:",squash"`
URI string
StandbyPower float64
}{}
Cache time.Duration
}{
Cache: time.Second,
}

if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}

return NewHomeWizard(cc.embed, cc.URI, cc.StandbyPower)
return NewHomeWizard(cc.embed, cc.URI, cc.StandbyPower, cc.Cache)
}

// NewHomeWizard creates HomeWizard charger
func NewHomeWizard(embed embed, uri string, standbypower float64) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri)
func NewHomeWizard(embed embed, uri string, standbypower float64, cache time.Duration) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri, cache)
if err != nil {
return nil, err
}
Expand All @@ -52,7 +53,7 @@ func NewHomeWizard(embed embed, uri string, standbypower float64) (*HomeWizard,

// Check compatible product type
if c.conn.ProductType != "HWE-SKT" {
return nil, errors.New("not supported product type: " + c.conn.ProductType)
return nil, errors.New("unsupported product type: " + c.conn.ProductType)
}

c.switchSocket = NewSwitchSocket(&embed, c.Enabled, c.conn.CurrentPower, standbypower)
Expand All @@ -62,34 +63,12 @@ func NewHomeWizard(embed embed, uri string, standbypower float64) (*HomeWizard,

// Enabled implements the api.Charger interface
func (c *HomeWizard) Enabled() (bool, error) {
var res homewizard.StateResponse
err := c.conn.GetJSON(fmt.Sprintf("%s/data", c.conn.URI), &res)
return res.PowerOn, err
return c.conn.Enabled()
}

// Enable implements the api.Charger interface
func (c *HomeWizard) Enable(enable bool) error {
var res homewizard.StateResponse
data := map[string]interface{}{
"power_on": enable,
}

req, err := request.New(http.MethodPut, fmt.Sprintf("%s/state", c.conn.URI), request.MarshalJSON(data), request.JSONEncoding)
if err != nil {
return err
}
if err := c.conn.DoJSON(req, &res); err != nil {
return err
}

switch {
case enable && !res.PowerOn:
return errors.New("switchOn failed")
case !enable && res.PowerOn:
return errors.New("switchOff failed")
default:
return nil
}
return c.conn.Enable(enable)
}

var _ api.MeterEnergy = (*HomeWizard)(nil)
Expand Down
17 changes: 11 additions & 6 deletions meter/homewizard.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package meter

import (
"time"

"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/meter/homewizard"
"github.com/evcc-io/evcc/util"
Expand All @@ -19,19 +21,22 @@ func init() {
// NewHomeWizardFromConfig creates a HomeWizard meter from generic config
func NewHomeWizardFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
URI string
}{}
URI string
Cache time.Duration
}{
Cache: time.Second,
}

if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}

return NewHomeWizard(cc.URI)
return NewHomeWizard(cc.URI, cc.Cache)
}

// NewHomeWizard creates HomeWizard meter
func NewHomeWizard(uri string) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri)
func NewHomeWizard(uri string, cache time.Duration) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri, cache)
if err != nil {
return nil, err
}
Expand All @@ -40,7 +45,7 @@ func NewHomeWizard(uri string) (*HomeWizard, error) {
conn: conn,
}

return c, err
return c, nil
}

var _ api.Meter = (*HomeWizard)(nil)
Expand Down
77 changes: 64 additions & 13 deletions meter/homewizard/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package homewizard
import (
"errors"
"fmt"
"net/http"
"strings"
"time"

"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
Expand All @@ -13,49 +16,97 @@ import (
// Connection is the homewizard connection
type Connection struct {
*request.Helper
URI string
uri string
ProductType string
dataCache provider.Cacheable[DataResponse]
stateCache provider.Cacheable[StateResponse]
}

// NewConnection creates a homewizard connection
func NewConnection(uri string) (*Connection, error) {
func NewConnection(uri string, cache time.Duration) (*Connection, error) {
if uri == "" {
return nil, errors.New("missing uri")
}

log := util.NewLogger("homewizard")
c := &Connection{
Helper: request.NewHelper(log),
URI: fmt.Sprintf("%s/api", util.DefaultScheme(strings.TrimRight(uri, "/"), "http")),
uri: fmt.Sprintf("%s/api", util.DefaultScheme(strings.TrimRight(uri, "/"), "http")),
}

c.Client.Transport = request.NewTripper(log, transport.Insecure())

// Check and set API version + product type
// check and set API version + product type
var res ApiResponse
if err := c.GetJSON(c.URI, &res); err != nil {
if err := c.GetJSON(c.uri, &res); err != nil {
return c, err
}
if res.ApiVersion != "v1" {
return nil, errors.New("not supported api version: " + res.ApiVersion)
return nil, errors.New("unsupported api version: " + res.ApiVersion)
}

c.URI = c.URI + "/" + res.ApiVersion
c.uri = c.uri + "/" + res.ApiVersion
c.ProductType = res.ProductType

c.dataCache = provider.ResettableCached(func() (DataResponse, error) {
var res DataResponse
err := c.GetJSON(fmt.Sprintf("%s/data", c.uri), &res)
return res, err
}, cache)

c.stateCache = provider.ResettableCached(func() (StateResponse, error) {
var res StateResponse
err := c.GetJSON(fmt.Sprintf("%s/state", c.uri), &res)
return res, err
}, cache)

return c, nil
}

// Enable implements the api.Charger interface
func (c *Connection) Enable(enable bool) error {
var res StateResponse
data := map[string]interface{}{
"power_on": enable,
}

req, err := request.New(http.MethodPut, fmt.Sprintf("%s/state", c.uri), request.MarshalJSON(data), request.JSONEncoding)
if err != nil {
return err
}
if err := c.DoJSON(req, &res); err != nil {
return err
}

if err == nil {
c.stateCache.Reset()
c.dataCache.Reset()
}

switch {
case enable && !res.PowerOn:
return errors.New("switchOn failed")
case !enable && res.PowerOn:
return errors.New("switchOff failed")
default:
return nil
}
}

// Enabled reads the homewizard switch state true=on/false=off
func (c *Connection) Enabled() (bool, error) {
res, err := c.stateCache.Get()
return res.PowerOn, err
}

// CurrentPower implements the api.Meter interface
func (d *Connection) CurrentPower() (float64, error) {
var res DataResponse
err := d.GetJSON(fmt.Sprintf("%s/data", d.URI), &res)
func (c *Connection) CurrentPower() (float64, error) {
res, err := c.dataCache.Get()
return res.ActivePowerW, err
}

// TotalEnergy implements the api.MeterEnergy interface
func (d *Connection) TotalEnergy() (float64, error) {
var res DataResponse
err := d.GetJSON(fmt.Sprintf("%s/data", d.URI), &res)
func (c *Connection) TotalEnergy() (float64, error) {
res, err := c.dataCache.Get()
return res.TotalPowerImportT1kWh + res.TotalPowerImportT2kWh + res.TotalPowerImportT3kWh + res.TotalPowerImportT4kWh, err
}

0 comments on commit 0c9dbe0

Please sign in to comment.