package tapo import ( "fmt" "strings" "github.com/evcc-io/evcc/api" "github.com/evcc-io/evcc/util" "github.com/insomniacslk/tapo" ) // Connection is the Tapo connection type Connection struct { log *util.Logger plug tapo.Plug lasttodayenergy int64 energy int64 } // NewConnection creates a new Tapo device connection. // User is encoded by using MessageDigest of SHA1 which is afterwards B64 encoded. // Password is directly B64 encoded. func NewConnection(host, user, password string) (*Connection, error) { if host == "" { return nil, fmt.Errorf("missing host") } if user == "" || password == "" { return nil, api.ErrMissingCredentials } log := util.NewLogger("tapo").Redact(user, password) plug := tapo.NewPlug(host, nil) if err := plug.Handshake(user, password); err != nil { return nil, fmt.Errorf("login failed: %w", err) } conn := &Connection{ log: log, plug: *plug, } res, err := conn.plug.GetDeviceInfo() if err != nil { return nil, err } conn.log.DEBUG.Printf("%s %s connected (fw:%s,hw:%s,mac:%s)", res.Type, res.Model, res.FWVersion, res.HWVersion, res.MAC) return conn, nil } // Enable implements the api.Charger interface func (c *Connection) Enable(enable bool) error { return c.plug.SetDeviceInfo(enable) } // Enabled implements the api.Charger interface func (c *Connection) Enabled() (bool, error) { return c.plug.IsOn() } // CurrentPower provides current power consuption func (c *Connection) CurrentPower() (float64, error) { resp, err := c.plug.GetEnergyUsage() if err != nil { return 0, c.checkMeterError(err) } return float64(resp.CurrentPower) / 1e3, nil } // ChargedEnergy collects the daily charged energy func (c *Connection) ChargedEnergy() (float64, error) { resp, err := c.plug.GetEnergyUsage() if err != nil { return 0, c.checkMeterError(err) } if int64(resp.TodayEnergy) > c.lasttodayenergy { c.energy += (int64(resp.TodayEnergy) - c.lasttodayenergy) } c.lasttodayenergy = int64(resp.TodayEnergy) return float64(c.energy) / 1000, nil } // checkMeterError checks for missing meter error func (c *Connection) checkMeterError(err error) error { if strings.Contains(err.Error(), "-1001") { return nil } return err }