Tasmota: enable multi-relay devices (#3368)

This commit is contained in:
Markus Thierolf 2022-05-17 11:39:54 +02:00 • committed by GitHub
parent 31eb3e25fd
commit ee8d3db356
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 168 additions and 25 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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