Tasmota: enable meter/relay channel-list (#10171)
This commit is contained in:
parent
d212f04b28
commit
8d8ac5c72e
8 changed files with 305 additions and 106 deletions
|
|
@ -15,8 +15,7 @@ import (
|
|||
|
||||
// Tasmota charger implementation
|
||||
type Tasmota struct {
|
||||
conn *tasmota.Connection
|
||||
channel int
|
||||
conn *tasmota.Connection
|
||||
*switchSocket
|
||||
}
|
||||
|
||||
|
|
@ -32,10 +31,10 @@ func NewTasmotaFromConfig(other map[string]interface{}) (api.Charger, error) {
|
|||
User string
|
||||
Password string
|
||||
StandbyPower float64
|
||||
Channel int
|
||||
Channel []int
|
||||
Cache time.Duration
|
||||
}{
|
||||
Channel: 1,
|
||||
Channel: []int{1},
|
||||
Cache: time.Second,
|
||||
}
|
||||
|
||||
|
|
@ -47,20 +46,19 @@ func NewTasmotaFromConfig(other map[string]interface{}) (api.Charger, error) {
|
|||
}
|
||||
|
||||
// NewTasmota creates Tasmota charger
|
||||
func NewTasmota(embed embed, uri, user, password string, channel int, standbypower float64, cache time.Duration) (*Tasmota, error) {
|
||||
conn, err := tasmota.NewConnection(uri, user, password, channel, cache)
|
||||
func NewTasmota(embed embed, uri, user, password string, channels []int, standbypower float64, cache time.Duration) (*Tasmota, error) {
|
||||
conn, err := tasmota.NewConnection(uri, user, password, channels, cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Tasmota{
|
||||
conn: conn,
|
||||
channel: channel,
|
||||
conn: conn,
|
||||
}
|
||||
|
||||
c.switchSocket = NewSwitchSocket(&embed, c.Enabled, c.conn.CurrentPower, standbypower)
|
||||
|
||||
return c, c.conn.ChannelExists(channel)
|
||||
return c, c.conn.RelayExists()
|
||||
}
|
||||
|
||||
// Enabled implements the api.Charger interface
|
||||
|
|
@ -79,3 +77,17 @@ var _ api.MeterEnergy = (*Tasmota)(nil)
|
|||
func (c *Tasmota) TotalEnergy() (float64, error) {
|
||||
return c.conn.TotalEnergy()
|
||||
}
|
||||
|
||||
var _ api.PhaseCurrents = (*Tasmota)(nil)
|
||||
|
||||
// Currents implements the api.PhaseCurrents interface
|
||||
func (c *Tasmota) Currents() (float64, float64, float64, error) {
|
||||
return c.conn.Currents()
|
||||
}
|
||||
|
||||
var _ api.PhaseVoltages = (*Tasmota)(nil)
|
||||
|
||||
// Voltages implements the api.PhaseVoltages interface
|
||||
func (c *Tasmota) Voltages() (float64, float64, float64, error) {
|
||||
return c.conn.Voltages()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
|
|
@ -19,17 +20,19 @@ func init() {
|
|||
registry.Add("tasmota", NewTasmotaFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateTasmota -b *Tasmota -r api.Meter -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)"
|
||||
|
||||
// NewTasmotaFromConfig creates a Tasmota meter from generic config
|
||||
func NewTasmotaFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
URI string
|
||||
User string
|
||||
Password string
|
||||
Channel int
|
||||
Channel []int
|
||||
Usage string
|
||||
Cache time.Duration
|
||||
}{
|
||||
Channel: 1,
|
||||
Channel: []int{1},
|
||||
Cache: time.Second,
|
||||
}
|
||||
|
||||
|
|
@ -37,12 +40,12 @@ func NewTasmotaFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return NewTasmota(cc.URI, cc.User, cc.Password, cc.Usage, cc.Channel, cc.Cache)
|
||||
return NewTasmota(cc.URI, cc.User, cc.Password, strings.ToLower(cc.Usage), cc.Channel, cc.Cache)
|
||||
}
|
||||
|
||||
// NewTasmota creates Tasmota meter
|
||||
func NewTasmota(uri, user, password, usage string, channel int, cache time.Duration) (*Tasmota, error) {
|
||||
conn, err := tasmota.NewConnection(uri, user, password, channel, cache)
|
||||
func NewTasmota(uri, user, password, usage string, channels []int, cache time.Duration) (api.Meter, error) {
|
||||
conn, err := tasmota.NewConnection(uri, user, password, channels, cache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -52,7 +55,13 @@ func NewTasmota(uri, user, password, usage string, channel int, cache time.Durat
|
|||
usage: usage,
|
||||
}
|
||||
|
||||
return c, err
|
||||
var currents, voltages func() (float64, float64, float64, error)
|
||||
if usage != "grid" {
|
||||
currents = c.currents
|
||||
voltages = c.voltages
|
||||
}
|
||||
|
||||
return decorateTasmota(c, currents, voltages), nil
|
||||
}
|
||||
|
||||
var _ api.Meter = (*Tasmota)(nil)
|
||||
|
|
@ -74,3 +83,13 @@ func (c *Tasmota) TotalEnergy() (float64, error) {
|
|||
}
|
||||
return c.conn.TotalEnergy()
|
||||
}
|
||||
|
||||
// currents implements the api.PhaseCurrents interface
|
||||
func (c *Tasmota) currents() (float64, float64, float64, error) {
|
||||
return c.conn.Currents()
|
||||
}
|
||||
|
||||
// voltages implements the api.PhaseVoltages interface
|
||||
func (c *Tasmota) voltages() (float64, float64, float64, error) {
|
||||
return c.conn.Voltages()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,24 +17,35 @@ import (
|
|||
type Connection struct {
|
||||
*request.Helper
|
||||
uri, user, password string
|
||||
channel int
|
||||
channels []int
|
||||
statusSnsG provider.Cacheable[StatusSNSResponse]
|
||||
statusStsG provider.Cacheable[StatusSTSResponse]
|
||||
}
|
||||
|
||||
// NewConnection creates a Tasmota connection
|
||||
func NewConnection(uri, user, password string, channel int, cache time.Duration) (*Connection, error) {
|
||||
func NewConnection(uri, user, password string, channels []int, cache time.Duration) (*Connection, error) {
|
||||
if uri == "" {
|
||||
return nil, errors.New("missing uri")
|
||||
}
|
||||
|
||||
used := make(map[int]bool)
|
||||
for _, c := range channels {
|
||||
if c < 1 || c > 8 {
|
||||
return nil, fmt.Errorf("invalid channel: %d", c)
|
||||
}
|
||||
if used[c] {
|
||||
return nil, fmt.Errorf("duplicate channel: %d", c)
|
||||
}
|
||||
used[c] = true
|
||||
}
|
||||
|
||||
log := util.NewLogger("tasmota")
|
||||
c := &Connection{
|
||||
Helper: request.NewHelper(log),
|
||||
uri: util.DefaultScheme(strings.TrimRight(uri, "/"), "http"),
|
||||
user: user,
|
||||
password: password,
|
||||
channel: channel,
|
||||
channels: channels,
|
||||
}
|
||||
|
||||
c.Client.Transport = request.NewTripper(log, transport.Insecure())
|
||||
|
|
@ -65,34 +76,36 @@ func NewConnection(uri, user, password string, channel int, cache time.Duration)
|
|||
}
|
||||
|
||||
// channelExists checks the existence of the configured relay channel interface
|
||||
func (c *Connection) ChannelExists(channel int) error {
|
||||
func (c *Connection) RelayExists() error {
|
||||
res, err := c.statusStsG.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
switch channel {
|
||||
case 1:
|
||||
ok = res.StatusSTS.Power != "" || res.StatusSTS.Power1 != ""
|
||||
case 2:
|
||||
ok = res.StatusSTS.Power2 != ""
|
||||
case 3:
|
||||
ok = res.StatusSTS.Power3 != ""
|
||||
case 4:
|
||||
ok = res.StatusSTS.Power4 != ""
|
||||
case 5:
|
||||
ok = res.StatusSTS.Power5 != ""
|
||||
case 6:
|
||||
ok = res.StatusSTS.Power6 != ""
|
||||
case 7:
|
||||
ok = res.StatusSTS.Power7 != ""
|
||||
case 8:
|
||||
ok = res.StatusSTS.Power8 != ""
|
||||
}
|
||||
for _, channel := range c.channels {
|
||||
switch channel {
|
||||
case 1:
|
||||
ok = res.StatusSTS.Power != "" || res.StatusSTS.Power1 != ""
|
||||
case 2:
|
||||
ok = res.StatusSTS.Power2 != ""
|
||||
case 3:
|
||||
ok = res.StatusSTS.Power3 != ""
|
||||
case 4:
|
||||
ok = res.StatusSTS.Power4 != ""
|
||||
case 5:
|
||||
ok = res.StatusSTS.Power5 != ""
|
||||
case 6:
|
||||
ok = res.StatusSTS.Power6 != ""
|
||||
case 7:
|
||||
ok = res.StatusSTS.Power7 != ""
|
||||
case 8:
|
||||
ok = res.StatusSTS.Power8 != ""
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid relay channel: %d", channel)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid relay channel: %d", channel)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -100,53 +113,56 @@ func (c *Connection) ChannelExists(channel int) error {
|
|||
|
||||
// Enable implements the api.Charger interface
|
||||
func (c *Connection) Enable(enable bool) error {
|
||||
cmd := fmt.Sprintf("Power%d off", c.channel)
|
||||
if enable {
|
||||
cmd = fmt.Sprintf("Power%d on", c.channel)
|
||||
}
|
||||
for _, channel := range c.channels {
|
||||
|
||||
parameters := url.Values{
|
||||
"user": []string{c.user},
|
||||
"password": []string{c.password},
|
||||
"cmnd": []string{cmd},
|
||||
}
|
||||
cmd := fmt.Sprintf("Power%d off", channel)
|
||||
if enable {
|
||||
cmd = fmt.Sprintf("Power%d on", channel)
|
||||
}
|
||||
|
||||
var res PowerResponse
|
||||
if err := c.GetJSON(fmt.Sprintf("%s/cm?%s", c.uri, parameters.Encode()), &res); err != nil {
|
||||
return err
|
||||
}
|
||||
parameters := url.Values{
|
||||
"user": []string{c.user},
|
||||
"password": []string{c.password},
|
||||
"cmnd": []string{cmd},
|
||||
}
|
||||
|
||||
var on bool
|
||||
switch c.channel {
|
||||
case 2:
|
||||
on = strings.ToUpper(res.Power2) == "ON"
|
||||
case 3:
|
||||
on = strings.ToUpper(res.Power3) == "ON"
|
||||
case 4:
|
||||
on = strings.ToUpper(res.Power4) == "ON"
|
||||
case 5:
|
||||
on = strings.ToUpper(res.Power5) == "ON"
|
||||
case 6:
|
||||
on = strings.ToUpper(res.Power6) == "ON"
|
||||
case 7:
|
||||
on = strings.ToUpper(res.Power7) == "ON"
|
||||
case 8:
|
||||
on = strings.ToUpper(res.Power8) == "ON"
|
||||
default:
|
||||
on = strings.ToUpper(res.Power) == "ON" || strings.ToUpper(res.Power1) == "ON"
|
||||
var res PowerResponse
|
||||
if err := c.GetJSON(fmt.Sprintf("%s/cm?%s", c.uri, parameters.Encode()), &res); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var enabled bool
|
||||
switch channel {
|
||||
case 2:
|
||||
enabled = strings.ToUpper(res.Power2) == "ON"
|
||||
case 3:
|
||||
enabled = strings.ToUpper(res.Power3) == "ON"
|
||||
case 4:
|
||||
enabled = strings.ToUpper(res.Power4) == "ON"
|
||||
case 5:
|
||||
enabled = strings.ToUpper(res.Power5) == "ON"
|
||||
case 6:
|
||||
enabled = strings.ToUpper(res.Power6) == "ON"
|
||||
case 7:
|
||||
enabled = strings.ToUpper(res.Power7) == "ON"
|
||||
case 8:
|
||||
enabled = strings.ToUpper(res.Power8) == "ON"
|
||||
default:
|
||||
enabled = strings.ToUpper(res.Power) == "ON" || strings.ToUpper(res.Power1) == "ON"
|
||||
}
|
||||
|
||||
switch {
|
||||
case enable && !enabled:
|
||||
return errors.New("switchOn failed")
|
||||
case !enable && enabled:
|
||||
return errors.New("switchOff failed")
|
||||
}
|
||||
}
|
||||
|
||||
c.statusSnsG.Reset()
|
||||
c.statusStsG.Reset()
|
||||
|
||||
switch {
|
||||
case enable && !on:
|
||||
return errors.New("switchOn failed")
|
||||
case !enable && on:
|
||||
return errors.New("switchOff failed")
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enabled implements the api.Charger interface
|
||||
|
|
@ -156,33 +172,46 @@ func (c *Connection) Enabled() (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
switch c.channel {
|
||||
case 2:
|
||||
return strings.ToUpper(res.StatusSTS.Power2) == "ON", err
|
||||
case 3:
|
||||
return strings.ToUpper(res.StatusSTS.Power3) == "ON", err
|
||||
case 4:
|
||||
return strings.ToUpper(res.StatusSTS.Power4) == "ON", err
|
||||
case 5:
|
||||
return strings.ToUpper(res.StatusSTS.Power5) == "ON", err
|
||||
case 6:
|
||||
return strings.ToUpper(res.StatusSTS.Power6) == "ON", err
|
||||
case 7:
|
||||
return strings.ToUpper(res.StatusSTS.Power7) == "ON", err
|
||||
case 8:
|
||||
return strings.ToUpper(res.StatusSTS.Power8) == "ON", err
|
||||
default:
|
||||
return strings.ToUpper(res.StatusSTS.Power) == "ON" || strings.ToUpper(res.StatusSTS.Power1) == "ON", err
|
||||
var enabled bool
|
||||
for _, channel := range c.channels {
|
||||
switch channel {
|
||||
case 2:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power2) == "ON"
|
||||
case 3:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power3) == "ON"
|
||||
case 4:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power4) == "ON"
|
||||
case 5:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power5) == "ON"
|
||||
case 6:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power6) == "ON"
|
||||
case 7:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power7) == "ON"
|
||||
case 8:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power8) == "ON"
|
||||
default:
|
||||
enabled = strings.ToUpper(res.StatusSTS.Power) == "ON" || strings.ToUpper(res.StatusSTS.Power1) == "ON"
|
||||
|
||||
}
|
||||
}
|
||||
return enabled, nil
|
||||
}
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
func (c *Connection) CurrentPower() (float64, error) {
|
||||
res, err := c.statusSnsG.Get()
|
||||
s, err := c.statusSnsG.Get()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.StatusSNS.Energy.Power.Channel(c.channel)
|
||||
var res float64
|
||||
for _, channel := range c.channels {
|
||||
power, err := s.StatusSNS.Energy.Power.Channel(channel)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
res += power
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// TotalEnergy implements the api.MeterEnergy interface
|
||||
|
|
@ -191,6 +220,42 @@ func (c *Connection) TotalEnergy() (float64, error) {
|
|||
return res.StatusSNS.Energy.Total, err
|
||||
}
|
||||
|
||||
// Currents implements the api.PhaseCurrents interface
|
||||
func (c *Connection) Currents() (float64, float64, float64, error) {
|
||||
s, err := c.statusSnsG.Get()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
var res [3]float64
|
||||
for i, c := range c.channels {
|
||||
res[i], err = s.StatusSNS.Energy.Current.Channel(c)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return res[0], res[1], res[2], err
|
||||
}
|
||||
|
||||
// Voltages implements the api.PhaseVoltages interface
|
||||
func (c *Connection) Voltages() (float64, float64, float64, error) {
|
||||
s, err := c.statusSnsG.Get()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
var res [3]float64
|
||||
for i, c := range c.channels {
|
||||
res[i], err = s.StatusSNS.Energy.Voltage.Channel(c)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return res[0], res[1], res[2], err
|
||||
}
|
||||
|
||||
// SmlPower provides the sml sensor power
|
||||
func (c *Connection) SmlPower() (float64, error) {
|
||||
res, err := c.statusSnsG.Get()
|
||||
|
|
|
|||
69
meter/tasmota_decorators.go
Normal file
69
meter/tasmota_decorators.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package meter
|
||||
|
||||
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/evcc-io/evcc/api"
|
||||
)
|
||||
|
||||
func decorateTasmota(base *Tasmota, phaseVoltages func() (float64, float64, float64, error), phaseCurrents func() (float64, float64, float64, error)) api.Meter {
|
||||
switch {
|
||||
case phaseCurrents == nil && phaseVoltages == nil:
|
||||
return base
|
||||
|
||||
case phaseCurrents == nil && phaseVoltages != nil:
|
||||
return &struct {
|
||||
*Tasmota
|
||||
api.PhaseVoltages
|
||||
}{
|
||||
Tasmota: base,
|
||||
PhaseVoltages: &decorateTasmotaPhaseVoltagesImpl{
|
||||
phaseVoltages: phaseVoltages,
|
||||
},
|
||||
}
|
||||
|
||||
case phaseCurrents != nil && phaseVoltages == nil:
|
||||
return &struct {
|
||||
*Tasmota
|
||||
api.PhaseCurrents
|
||||
}{
|
||||
Tasmota: base,
|
||||
PhaseCurrents: &decorateTasmotaPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
}
|
||||
|
||||
case phaseCurrents != nil && phaseVoltages != nil:
|
||||
return &struct {
|
||||
*Tasmota
|
||||
api.PhaseCurrents
|
||||
api.PhaseVoltages
|
||||
}{
|
||||
Tasmota: base,
|
||||
PhaseCurrents: &decorateTasmotaPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
PhaseVoltages: &decorateTasmotaPhaseVoltagesImpl{
|
||||
phaseVoltages: phaseVoltages,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type decorateTasmotaPhaseCurrentsImpl struct {
|
||||
phaseCurrents func() (float64, float64, float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateTasmotaPhaseCurrentsImpl) Currents() (float64, float64, float64, error) {
|
||||
return impl.phaseCurrents()
|
||||
}
|
||||
|
||||
type decorateTasmotaPhaseVoltagesImpl struct {
|
||||
phaseVoltages func() (float64, float64, float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateTasmotaPhaseVoltagesImpl) Voltages() (float64, float64, float64, error) {
|
||||
return impl.phaseVoltages()
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
template: tasmota
|
||||
products:
|
||||
- brand: Tasmota
|
||||
- brand: Tasmota (1 Phase Charger)
|
||||
group: switchsockets
|
||||
params:
|
||||
- name: host
|
||||
|
|
@ -16,11 +16,11 @@ params:
|
|||
default: 1
|
||||
required: true
|
||||
description:
|
||||
de: Nummer des Schaltkanals
|
||||
en: Device relay channel number
|
||||
de: Schaltkanal Nummer
|
||||
en: Relaychannel number
|
||||
help:
|
||||
de: Nummer des Schaltkanals (1-8), bei Geräten mit mehr als einem Schalter
|
||||
en: Device relay channel number (allowed range 1-8)
|
||||
de: Schaltkanal (1-8)
|
||||
en: Relaychannel number (1-8)
|
||||
- preset: switchsocket
|
||||
render: |
|
||||
type: tasmota
|
||||
|
|
@ -31,5 +31,5 @@ render: |
|
|||
{{- if .password }}
|
||||
password: {{ .password }}
|
||||
{{- end }}
|
||||
channel: {{ .channel }} # relay channel (allowed range: 1-8)
|
||||
channel: [{{ .channel }}] # list of relay channels [1,2,....,8]
|
||||
{{ include "switchsocket" . }}
|
||||
|
|
|
|||
24
templates/definition/meter/tasmota-3p.yaml
Normal file
24
templates/definition/meter/tasmota-3p.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
template: tasmota-3p
|
||||
products:
|
||||
- description:
|
||||
generic: Tasmota (3 Phase Meter - meter channels 1,2+3 must be used)
|
||||
group: switchsockets
|
||||
params:
|
||||
- name: usage
|
||||
choice: ["grid", "pv", "battery", "charge"]
|
||||
- name: host
|
||||
- name: user
|
||||
required: false
|
||||
help:
|
||||
de: Standard-User ist admin
|
||||
en: admin is default
|
||||
- name: password
|
||||
required: false
|
||||
mask: true
|
||||
render: |
|
||||
type: tasmota
|
||||
uri: http://{{ .host }}
|
||||
usage: {{ .usage }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
channel: [1,2,3] # list of meter channels [1,2,....,8]
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
template: tasmota
|
||||
products:
|
||||
- description:
|
||||
generic: Tasmota
|
||||
generic: Tasmota (1 Phase Meter)
|
||||
group: switchsockets
|
||||
params:
|
||||
- name: usage
|
||||
choice: ["grid", "pv", "battery"]
|
||||
choice: ["grid", "pv", "battery", "charge"]
|
||||
- name: host
|
||||
- name: user
|
||||
required: false
|
||||
|
|
@ -15,9 +15,19 @@ params:
|
|||
- name: password
|
||||
required: false
|
||||
mask: true
|
||||
- name: channel
|
||||
default: 1
|
||||
required: true
|
||||
description:
|
||||
de: Kanal Nummer
|
||||
en: Channel number
|
||||
help:
|
||||
de: Meterkanal (1-8)
|
||||
en: Meterchannel number (1-8)
|
||||
render: |
|
||||
type: tasmota
|
||||
uri: http://{{ .host }}
|
||||
usage: {{ .usage }}
|
||||
user: {{ .user }}
|
||||
password: {{ .password }}
|
||||
channel: [{{ .channel }}] # list of meter channels [1,2,....,8]
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
"HomeWizard",
|
||||
"myStrom",
|
||||
"Shelly",
|
||||
"Tasmota",
|
||||
"Tasmota (1 Phase Charger)",
|
||||
"TP-Link"
|
||||
],
|
||||
"Meters": [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue