HomeAssistant: add phase switching (#27896)

This commit is contained in:
JANMCFLY98 2026-04-06 16:22:36 +02:00 • committed by GitHub
parent 3967594b79
commit c785c54bbe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 3 deletions

View file

@ -1,11 +1,12 @@
package charger
//go:generate go tool decorate -f decorateHomeAssistant -b *HomeAssistant -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages
//go:generate go tool decorate -f decorateHomeAssistant -b *HomeAssistant -r api.Charger -t api.Meter,api.MeterEnergy,api.PhaseCurrents,api.PhaseVoltages,api.PhaseSwitcher,api.PhaseGetter
// -t api.CurrentGetter
import (
"errors"
"fmt"
"strconv"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
@ -39,6 +40,7 @@ func NewHomeAssistantFromConfig(other map[string]any) (api.Charger, error) {
Energy string // optional - energy sensor
Currents []string // optional - current sensors for L1, L2, L3
Voltages []string // optional - voltage sensors for L1, L2, L3
Phases string // optional - select entity for 1p/3p phase switching
}
if err := util.DecodeOther(other, &cc); err != nil {
@ -76,6 +78,8 @@ func NewHomeAssistantFromConfig(other map[string]any) (api.Charger, error) {
// decorators for optional interfaces
var power, energy func() (float64, error)
var currents, voltages func() (float64, float64, float64, error)
var phases1p3p func(int) error
var phasesG func() (int, error)
if cc.Power != "" {
power = func() (float64, error) { return conn.GetFloatState(cc.Power) }
@ -98,7 +102,22 @@ func NewHomeAssistantFromConfig(other map[string]any) (api.Charger, error) {
return nil, fmt.Errorf("voltages: %w", err)
}
return decorateHomeAssistant(c, power, energy, currents, voltages), nil
// phase switching (optional)
if cc.Phases != "" {
phases1p3p = func(phases int) error {
return conn.CallSelectService(cc.Phases, strconv.Itoa(phases))
}
phasesG = func() (int, error) {
val, err := conn.GetIntState(cc.Phases)
if err != nil {
return 0, err
}
return int(val), nil
}
}
return decorateHomeAssistant(c, power, energy, currents, voltages, phases1p3p, phasesG), nil
}
var _ api.Charger = (*HomeAssistant)(nil)

View file

@ -8,7 +8,7 @@ import (
"github.com/evcc-io/evcc/api"
)
func decorateHomeAssistant(base *HomeAssistant, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error)) api.Charger {
func decorateHomeAssistant(base *HomeAssistant, meter func() (float64, error), meterEnergy func() (float64, error), phaseCurrents func() (float64, float64, float64, error), phaseVoltages func() (float64, float64, float64, error), phaseSwitcher func(int) error, phaseGetter func() (int, error)) api.Charger {
caps := make(map[reflect.Type]any)
if meter != nil {
@ -27,6 +27,14 @@ func decorateHomeAssistant(base *HomeAssistant, meter func() (float64, error), m
caps[reflect.TypeFor[api.PhaseVoltages]()] = &decorateHomeAssistantPhaseVoltagesImpl{phaseVoltages: phaseVoltages}
}
if phaseSwitcher != nil {
caps[reflect.TypeFor[api.PhaseSwitcher]()] = &decorateHomeAssistantPhaseSwitcherImpl{phaseSwitcher: phaseSwitcher}
}
if phaseGetter != nil {
caps[reflect.TypeFor[api.PhaseGetter]()] = &decorateHomeAssistantPhaseGetterImpl{phaseGetter: phaseGetter}
}
if len(caps) == 0 {
return base
}
@ -68,6 +76,22 @@ func (impl *decorateHomeAssistantPhaseCurrentsImpl) Currents() (float64, float64
return impl.phaseCurrents()
}
type decorateHomeAssistantPhaseGetterImpl struct {
phaseGetter func() (int, error)
}
func (impl *decorateHomeAssistantPhaseGetterImpl) GetPhases() (int, error) {
return impl.phaseGetter()
}
type decorateHomeAssistantPhaseSwitcherImpl struct {
phaseSwitcher func(int) error
}
func (impl *decorateHomeAssistantPhaseSwitcherImpl) Phases1p3p(p0 int) error {
return impl.phaseSwitcher(p0)
}
type decorateHomeAssistantPhaseVoltagesImpl struct {
phaseVoltages func() (float64, float64, float64, error)
}

View file

@ -142,6 +142,17 @@ params:
help:
en: Entity ID for L3 voltage measurement in volts
de: Entitäts-ID für L3 Spannungsmessung in Volt
- name: phases
description:
de: Phasenumschaltungs-Entität
en: Phase switching entity
service: homeassistant/entities?uri={uri}&domain=select,input_select
example: "select.charger_phases"
advanced: true
help:
en: Entity ID for 1p/3p phase switching (select entity with options "1" and "3")
de: Entitäts-ID für 1p/3p Phasenumschaltung (Select-Entität mit Optionen "1" und "3")
render: |
type: homeassistant
uri: {{ .uri }}
@ -163,3 +174,6 @@ render: |
- {{ .voltageL2 }}
- {{ .voltageL3 }}
{{- end }}
{{- if .phases }}
phases: {{ .phases }}
{{- end }}

View file

@ -267,6 +267,21 @@ func (c *Connection) CallNumberService(entity string, value float64) error {
return c.CallService(domain, "set_value", data)
}
// CallSelectService is a convenience method for setting select entity options.
func (c *Connection) CallSelectService(entity, option string) error {
domain, err := domain(entity)
if err != nil {
return err
}
data := map[string]any{
"entity_id": entity,
"option": option,
}
return c.CallService(domain, "select_option", data)
}
// GetPhaseFloatStates retrieves three phase values (currents, voltages, etc.)
func (c *Connection) GetPhaseFloatStates(entities []string) (float64, float64, float64, error) {
if len(entities) != 3 {