Tasmota: enable multi-relay devices (#3368)
This commit is contained in:
parent
31eb3e25fd
commit
ee8d3db356
9 changed files with 168 additions and 25 deletions
|
|
@ -2,6 +2,7 @@ package charger
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/meter/tasmota"
|
||||
|
|
@ -30,17 +31,18 @@ func NewTasmotaFromConfig(other map[string]interface{}) (api.Charger, error) {
|
|||
User string
|
||||
Password string
|
||||
StandbyPower float64
|
||||
Channel int
|
||||
}{}
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewTasmota(cc.URI, cc.User, cc.Password, cc.StandbyPower)
|
||||
return NewTasmota(cc.URI, cc.User, cc.Password, cc.Channel, cc.StandbyPower)
|
||||
}
|
||||
|
||||
// NewTasmota creates Tasmota charger
|
||||
func NewTasmota(uri, user, password string, standbypower float64) (*Tasmota, error) {
|
||||
conn, err := tasmota.NewConnection(uri, user, password)
|
||||
func NewTasmota(uri, user, password string, channel int, standbypower float64) (*Tasmota, error) {
|
||||
conn, err := tasmota.NewConnection(uri, user, password, channel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -55,27 +57,69 @@ func NewTasmota(uri, user, password string, standbypower float64) (*Tasmota, err
|
|||
|
||||
// Enabled implements the api.Charger interface
|
||||
func (c *Tasmota) Enabled() (bool, error) {
|
||||
var res tasmota.StatusResponse
|
||||
var res tasmota.StatusSTSResponse
|
||||
err := c.conn.ExecCmd("Status 0", &res)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return res.Status.Power == 1, err
|
||||
switch c.conn.Channel {
|
||||
case 2:
|
||||
return res.StatusSTS.Power2 == "ON", err
|
||||
case 3:
|
||||
return res.StatusSTS.Power3 == "ON", err
|
||||
case 4:
|
||||
return res.StatusSTS.Power4 == "ON", err
|
||||
case 5:
|
||||
return res.StatusSTS.Power5 == "ON", err
|
||||
case 6:
|
||||
return res.StatusSTS.Power6 == "ON", err
|
||||
case 7:
|
||||
return res.StatusSTS.Power7 == "ON", err
|
||||
case 8:
|
||||
return res.StatusSTS.Power8 == "ON", err
|
||||
default:
|
||||
return res.StatusSTS.Power == "ON" || res.StatusSTS.Power1 == "ON", err
|
||||
}
|
||||
}
|
||||
|
||||
// Enable implements the api.Charger interface
|
||||
func (c *Tasmota) Enable(enable bool) error {
|
||||
var res tasmota.PowerResponse
|
||||
cmd := "Power off"
|
||||
on := false
|
||||
cmd := fmt.Sprintf("Power%d off", c.conn.Channel)
|
||||
if enable {
|
||||
cmd = "Power on"
|
||||
cmd = fmt.Sprintf("Power%d on", c.conn.Channel)
|
||||
}
|
||||
|
||||
err := c.conn.ExecCmd(cmd, &res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch c.conn.Channel {
|
||||
case 2:
|
||||
on = res.Power2 == "ON"
|
||||
case 3:
|
||||
on = res.Power3 == "ON"
|
||||
case 4:
|
||||
on = res.Power4 == "ON"
|
||||
case 5:
|
||||
on = res.Power5 == "ON"
|
||||
case 6:
|
||||
on = res.Power6 == "ON"
|
||||
case 7:
|
||||
on = res.Power7 == "ON"
|
||||
case 8:
|
||||
on = res.Power8 == "ON"
|
||||
default:
|
||||
on = res.Power == "ON" || res.Power1 == "ON"
|
||||
}
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
return err
|
||||
case enable && res.Power != "ON":
|
||||
case enable && !on:
|
||||
return errors.New("switchOn failed")
|
||||
case !enable && res.Power != "OFF":
|
||||
case !enable && on:
|
||||
return errors.New("switchOff failed")
|
||||
default:
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@ func NewTasmotaFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
URI string
|
||||
User string
|
||||
Password string
|
||||
Channel int
|
||||
}{}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tasmota.NewConnection(cc.URI, cc.User, cc.Password)
|
||||
return tasmota.NewConnection(cc.URI, cc.User, cc.Password, cc.Channel)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@ import (
|
|||
type Connection struct {
|
||||
*request.Helper
|
||||
uri, user, password string
|
||||
Channel int
|
||||
}
|
||||
|
||||
// NewConnection creates Tasmota charger
|
||||
func NewConnection(uri, user, password string) (*Connection, error) {
|
||||
func NewConnection(uri, user, password string, channel int) (*Connection, error) {
|
||||
if uri == "" {
|
||||
return nil, errors.New("missing uri")
|
||||
}
|
||||
|
|
@ -29,10 +30,16 @@ func NewConnection(uri, user, password string) (*Connection, error) {
|
|||
uri: util.DefaultScheme(strings.TrimRight(uri, "/"), "http"),
|
||||
user: user,
|
||||
password: password,
|
||||
Channel: channel,
|
||||
}
|
||||
|
||||
c.Client.Transport = request.NewTripper(log, transport.Insecure())
|
||||
|
||||
err := c.ChannelExists()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
|
@ -73,3 +80,40 @@ func (d *Connection) TotalEnergy() (float64, error) {
|
|||
|
||||
return float64(res.StatusSNS.Energy.Total), nil
|
||||
}
|
||||
|
||||
// ChannelExists checks the existence of the configured relay channel interface
|
||||
func (d *Connection) ChannelExists() error {
|
||||
var res *StatusSTSResponse
|
||||
err := d.ExecCmd("Status 0", &res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
channelexists := false
|
||||
switch d.Channel {
|
||||
case 1:
|
||||
channelexists = res.StatusSTS.Power != "" || res.StatusSTS.Power1 != ""
|
||||
case 2:
|
||||
channelexists = res.StatusSTS.Power2 != ""
|
||||
case 3:
|
||||
channelexists = res.StatusSTS.Power3 != ""
|
||||
case 4:
|
||||
channelexists = res.StatusSTS.Power4 != ""
|
||||
case 5:
|
||||
channelexists = res.StatusSTS.Power5 != ""
|
||||
case 6:
|
||||
channelexists = res.StatusSTS.Power6 != ""
|
||||
case 7:
|
||||
channelexists = res.StatusSTS.Power7 != ""
|
||||
case 8:
|
||||
channelexists = res.StatusSTS.Power8 != ""
|
||||
default:
|
||||
channelexists = false
|
||||
}
|
||||
|
||||
if !channelexists {
|
||||
return errors.New("configured relay channel doesn't exist on device")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package tasmota
|
||||
|
||||
// StatusResponse is a part of the Tasmota Status 0 command Status response
|
||||
// StatusResponse is the Status part of the Tasmota Status 0 command response
|
||||
// https://tasmota.github.io/docs/JSON-Status-Responses/
|
||||
type StatusResponse struct {
|
||||
Status struct {
|
||||
|
|
@ -26,6 +26,36 @@ type StatusResponse struct {
|
|||
}
|
||||
}
|
||||
|
||||
// StatusSTSResponse is the StatusSTS part of the Tasmota Status 0 command response
|
||||
// https://tasmota.github.io/docs/JSON-Status-Responses/
|
||||
type StatusSTSResponse struct {
|
||||
StatusSTS struct {
|
||||
Power string // ON, OFF, Error
|
||||
Power1 string // ON, OFF, Error
|
||||
Power2 string // ON, OFF, Error
|
||||
Power3 string // ON, OFF, Error
|
||||
Power4 string // ON, OFF, Error
|
||||
Power5 string // ON, OFF, Error
|
||||
Power6 string // ON, OFF, Error
|
||||
Power7 string // ON, OFF, Error
|
||||
Power8 string // ON, OFF, Error
|
||||
}
|
||||
}
|
||||
|
||||
// PowerResponse is the Tasmota Power command Status response
|
||||
// https://tasmota.github.io/docs/Commands/#with-web-requests
|
||||
type PowerResponse struct {
|
||||
Power string // ON, OFF, Error
|
||||
Power1 string // ON, OFF, Error
|
||||
Power2 string // ON, OFF, Error
|
||||
Power3 string // ON, OFF, Error
|
||||
Power4 string // ON, OFF, Error
|
||||
Power5 string // ON, OFF, Error
|
||||
Power6 string // ON, OFF, Error
|
||||
Power7 string // ON, OFF, Error
|
||||
Power8 string // ON, OFF, Error
|
||||
}
|
||||
|
||||
// StatusSNSResponse is the Tasmota Status 8 command Status response
|
||||
// https://tasmota.github.io/docs/JSON-Status-Responses/
|
||||
type StatusSNSResponse struct {
|
||||
|
|
@ -45,9 +75,3 @@ type StatusSNSResponse struct {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PowerResponse is the Tasmota Power command Status response
|
||||
// https://tasmota.github.io/docs/Commands/#with-web-requests
|
||||
type PowerResponse struct {
|
||||
Power string // ON, OFF, Error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,35 @@ products:
|
|||
- brand: Tasmota
|
||||
group: switchsockets
|
||||
params:
|
||||
- name: host
|
||||
- name: host
|
||||
- name: user
|
||||
required: false
|
||||
help:
|
||||
de: Standard-User ist admin
|
||||
en: admin is default
|
||||
- name: password
|
||||
required: false
|
||||
mask: true
|
||||
- name: channel
|
||||
default: 1
|
||||
required: true
|
||||
description:
|
||||
de: Nummer des Schalt-Kanals
|
||||
en: Device relay channel number
|
||||
help:
|
||||
de: Nummer des Schalt-Kanals (1-8), bei Geräten mit mehr als einem Schalter
|
||||
en: Device relay channel number (allowed range 1-8)
|
||||
- name: standbypower
|
||||
default: 15
|
||||
render: |
|
||||
type: tasmota
|
||||
uri: http://{{ .host }} # shelly device ip address (local)
|
||||
uri: http://{{ .host }}
|
||||
{{ if ne .user "" }}
|
||||
user: {{ .user }}
|
||||
{{ end }}
|
||||
{{ if ne .password "" }}
|
||||
password: {{ .password }}
|
||||
{{ end }}
|
||||
channel: {{ .channel }} # relay channel (allowed range: 1-8)
|
||||
standbypower: {{ .standbypower }}
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ params:
|
|||
valuetype: stringlist
|
||||
- name: standbypower
|
||||
description:
|
||||
de: Standby-Leistung in W"
|
||||
de: Standby-Leistung in W
|
||||
en: Standby power in W
|
||||
help:
|
||||
de: Leistung oberhalb des angegebenen Wertes wird als Ladeleistung gewertet
|
||||
|
|
|
|||
|
|
@ -9,12 +9,24 @@ params:
|
|||
- name: host
|
||||
- name: user
|
||||
required: false
|
||||
default: admin
|
||||
help:
|
||||
de: Standard-User ist admin
|
||||
en: admin is default
|
||||
- name: password
|
||||
required: false
|
||||
mask: true
|
||||
- name: channel
|
||||
default: 1
|
||||
required: true
|
||||
description:
|
||||
de: Nummer des Schalt-Kanals
|
||||
en: Device relay channel number
|
||||
help:
|
||||
de: Nummer des Schalt-Kanals (1-8), bei Geräten mit mehr als einem Schalter
|
||||
en: Device relay channel number (allowed range 1-8)
|
||||
render: |
|
||||
type: tasmota
|
||||
uri: http://{{ .host }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
channel: {{ .channel }} # # relay channel (allowed range: 1-8)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ render:
|
|||
type: template
|
||||
template: tasmota
|
||||
host: 192.0.2.2 # IP-Adresse oder Hostname
|
||||
user: # Benutzerkonto (bspw. E-Mail Adresse, User Id, etc.) # Optional
|
||||
user: # Standard-User ist admin # Optional
|
||||
password: # Passwort des Benutzerkontos (bei führenden Nullen bitte in einfache Hochkommata setzen) # Optional
|
||||
channel: 1 # Nummer des Schalt-Kanals (1-8), bei Geräten mit mehr als einem Schalter
|
||||
standbypower: 15 # Leistung oberhalb des angegebenen Wertes wird als Ladeleistung gewertet # Optional
|
||||
|
|
|
|||
|
|
@ -8,5 +8,6 @@ render:
|
|||
template: tasmota
|
||||
usage: pv
|
||||
host: 192.0.2.2 # IP-Adresse oder Hostname
|
||||
user: admin # Benutzerkonto (bspw. E-Mail Adresse, User Id, etc.) # Optional
|
||||
user: # Standard-User ist admin # Optional
|
||||
password: # Passwort des Benutzerkontos (bei führenden Nullen bitte in einfache Hochkommata setzen) # Optional
|
||||
channel: 1 # Nummer des Schalt-Kanals (1-8), bei Geräten mit mehr als einem Schalter
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue