evcc-io/plugin/gpio_linux.go
2025-12-07 16:40:56 +01:00

101 lines
1.9 KiB
Go

//go:build linux
package plugin
import (
"context"
"fmt"
"sync"
"github.com/evcc-io/evcc/util"
"github.com/stianeikeland/go-rpio/v4"
)
func init() {
registry.AddCtx("gpio", NewGpioPluginFromConfig)
}
type gpio struct {
mu sync.Mutex
typ GpioType
pin rpio.Pin
}
// NewGpioPluginFromConfig creates a GPIO provider
func NewGpioPluginFromConfig(ctx context.Context, other map[string]any) (Plugin, error) {
var cc struct {
Type GpioType
Pin int
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
p := &gpio{
typ: cc.Type,
pin: rpio.Pin(cc.Pin),
}
// initialize GPIO and set pins to input
if err := rpio.Open(); err != nil {
return nil, fmt.Errorf("failed to open GPIO: %w", err)
}
defer rpio.Close()
switch cc.Type {
case GpioTypeRead:
p.pin.Input()
case GpioTypeWrite:
p.pin.Output()
default:
return nil, fmt.Errorf("invalid type: %s", cc.Type)
}
return p, nil
}
var _ BoolGetter = (*gpio)(nil)
// BoolGetter returns GPIO pin active
func (p *gpio) BoolGetter() (func() (bool, error), error) {
if p.typ != GpioTypeRead {
return nil, fmt.Errorf("invalid gpio type: %s", p.typ)
}
return func() (bool, error) {
p.mu.Lock()
defer p.mu.Unlock()
if err := rpio.Open(); err != nil {
return false, fmt.Errorf("failed to open GPIO: %w", err)
}
defer rpio.Close()
return p.pin.Read() != rpio.Low, nil
}, nil
}
var _ BoolSetter = (*gpio)(nil)
// BoolSetter returns GPIO pin active
func (p *gpio) BoolSetter(_ string) (func(bool) error, error) {
if p.typ != GpioTypeWrite {
return nil, fmt.Errorf("invalid gpio type: %s", p.typ)
}
return func(b bool) error {
p.mu.Lock()
defer p.mu.Unlock()
if err := rpio.Open(); err != nil {
return fmt.Errorf("failed to open GPIO: %w", err)
}
defer rpio.Close()
val := map[bool]rpio.State{false: rpio.Low, true: rpio.High}[b]
p.pin.Write(val)
return nil
}, nil
}