Add Home Assistant Switch as switchsockets charger (#21358)

This commit is contained in:
Niklas 2025-05-25 10:59:21 +02:00 • committed by GitHub
parent 104538556d
commit 89c45015c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 175 additions and 0 deletions

View file

@ -0,0 +1,106 @@
package charger
import (
"fmt"
"net/http"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
)
type HomeAssistantSwitch struct {
baseURL string
switchEntity string
powerEntity string
*request.Helper
*switchSocket
}
func init() {
registry.Add("homeassistant-switch", NewHomeAssistantSwitchFromConfig)
}
func NewHomeAssistantSwitchFromConfig(other map[string]interface{}) (api.Charger, error) {
var cc struct {
embed `mapstructure:",squash"`
BaseURL string
Token string
SwitchEntity string
PowerEntity string
StandbyPower float64
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewHomeAssistantSwitch(cc.embed, cc.BaseURL, cc.Token, cc.SwitchEntity, cc.PowerEntity, cc.StandbyPower)
}
//go:generate go tool decorate -f decorateHomeAssistantSwitch -b *HomeAssistantSwitch -r api.Charger -t "api.Meter,CurrentPower,func() (float64, error)"
func NewHomeAssistantSwitch(embed embed, baseURL, token, switchEntity, powerEntity string, standbypower float64) (api.Charger, error) {
c := &HomeAssistantSwitch{
baseURL: baseURL,
switchEntity: switchEntity,
powerEntity: powerEntity,
Helper: request.NewHelper(util.NewLogger("ha-switch")),
}
c.switchSocket = NewSwitchSocket(&embed, c.Enabled, c.CurrentPower, standbypower)
c.Helper.Client.Transport = &transport.Decorator{
Decorator: transport.DecorateHeaders(map[string]string{
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
}),
Base: c.Helper.Client.Transport,
}
var currentPower func() (float64, error)
if powerEntity != "" {
currentPower = c.currentPower
}
return decorateHomeAssistantSwitch(c, currentPower), nil
}
// Enabled implements the api.Charger interface
func (c *HomeAssistantSwitch) Enabled() (bool, error) {
var res struct {
State string `json:"state"`
}
uri := fmt.Sprintf("%s/api/states/%s", c.baseURL, c.switchEntity)
err := c.Helper.GetJSON(uri, &res)
return res.State == "on", err
}
// Enable implements the api.Charger interface
func (c *HomeAssistantSwitch) Enable(enable bool) error {
service := "turn_off"
if enable {
service = "turn_on"
}
data := map[string]any{"entity_id": c.switchEntity}
uri := fmt.Sprintf("%s/api/services/switch/%s", c.baseURL, service)
req, _ := request.New(http.MethodPost, uri, request.MarshalJSON(data), request.JSONEncoding)
return c.Helper.DoJSON(req, nil)
}
// currentPower implements the api.Meter interface (optional)
func (c *HomeAssistantSwitch) currentPower() (float64, error) {
var res struct {
State float64 `json:"state,string"`
}
uri := fmt.Sprintf("%s/api/states/%s", c.baseURL, c.powerEntity)
err := c.Helper.GetJSON(uri, &res)
return res.State, err
}

View file

@ -0,0 +1,35 @@
package charger
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
import (
"github.com/evcc-io/evcc/api"
)
func decorateHomeAssistantSwitch(base *HomeAssistantSwitch, meter func() (float64, error)) api.Charger {
switch {
case meter == nil:
return base
case meter != nil:
return &struct {
*HomeAssistantSwitch
api.Meter
}{
HomeAssistantSwitch: base,
Meter: &decorateHomeAssistantSwitchMeterImpl{
meter: meter,
},
}
}
return nil
}
type decorateHomeAssistantSwitchMeterImpl struct {
meter func() (float64, error)
}
func (impl *decorateHomeAssistantSwitchMeterImpl) CurrentPower() (float64, error) {
return impl.meter()
}

View file

@ -0,0 +1,34 @@
template: homeassistant-switch
products:
- brand: Home Assistant Switch
group: switchsockets
params:
- name: baseurl
description:
de: Basis-URL der Home Assistant Instanz (z.B. http://homeassistant.local:8123)
en: Base URL of the Home Assistant instance (e.g. http://homeassistant.local:8123)
required: true
default: http://homeassistant.local:8123
- name: token
mask: true
required: true
description:
de: Long-Lived Access Token aus Home Assistant Profil
en: Long-Lived Access Token from Home Assistant profile
- name: switchentity
required: true
description:
de: Entity ID des schaltbaren Geräts (z.B. switch.smartsocket)
en: Entity ID of the switch device (e.g. switch.smartsocket)
- name: powerentity
description:
de: Entity ID für Leistungsmessung (optional, z.B. sensor.smartsocket_power)
en: Entity ID for power measurement (optional, e.g. sensor.smartsocket_power)
- preset: switchsocket
render: |
type: homeassistant-switch
baseurl: {{ .baseurl }}
token: {{ .token }}
switchentity: {{ .switchentity }}
powerentity: {{ .powerentity }}
{{ include "switchsocket" . }}