ratgdo

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2026 License: MIT Imports: 13 Imported by: 0

README

ratgdo-go

A Go client for ratgdo garage-door controllers running the upstream ESPHome firmware.

It speaks the ESPHome native API on TCP port 6053 with Noise-protocol encryption. The device must be flashed with an api.encryption.key; the matching base64 PSK is passed to Dial. Plaintext sessions are supported for trusted networks by passing an empty key.

The entity schema is hardcoded for ratgdo boards — one cover entity named door, one light, and the standard motion/obstruction/button sensors. Other ESPHome devices will not work.

Install

go get github.com/kevinburke/ratgdo-go
go install github.com/kevinburke/ratgdo-go/cmd/ratgdo@latest

Library use

ctx := context.Background()
client, err := ratgdo.Dial(ctx, "ratgdo.local:6053", os.Getenv("RATGDO_KEY"), nil)
if err != nil {
    log.Fatal(err)
}
defer client.Close()

if err := client.OpenDoor(ctx); err != nil {
    log.Fatal(err)
}

// React to state changes:
for ev := range client.Subscribe() {
    if ev.DoorFinishedClosing() {
        log.Printf("door closed at %s", ev.At)
    }
}

The Client is long-lived. After Dial returns it maintains the TCP session in the background, reconnecting with exponential backoff whenever the connection drops (device reboot, WiFi glitch, etc.). Commands issued while disconnected block until reconnection or the caller's context expires.

Client.State() returns a snapshot of the most recent observed state. Client.Subscribe() returns a channel that receives every state delta plus connect/disconnect events. Client.WaitFor(ctx, pred) blocks until a predicate over the state becomes true.

See the package docs for the full API.

Command-line tool

cmd/ratgdo is a small CLI built on top of the library. Configure the device address and encryption key via flags or environment variables:

export RATGDO_ADDRESS=ratgdo.local:6053
export RATGDO_KEY=<base64 PSK from the ESPHome config>

ratgdo info         # print device identity (model, MAC, ESPHome version)
ratgdo state        # print the current observed state and exit
ratgdo watch        # stream state changes until interrupted
ratgdo open
ratgdo close
ratgdo stop
ratgdo light-on
ratgdo light-off

Run ratgdo --help for the full flag list.

Firmware

The stock ratgdo firmware exposes an unauthenticated HTTP API, and its native ESPHome API has no encryption key by default. An example ESPHome overlay that enables Noise-encrypted native API access — plus a Makefile wrapping the ESPHome Docker image for compile/flash/logs — lives under scripts/. See scripts/README.md for the full build-and-flash walkthrough.

License

MIT

Documentation

Overview

Package ratgdo is a Go client for ratgdo garage-door controllers running the upstream ESPHome firmware (https://github.com/ratgdo/esphome-ratgdo).

It speaks the ESPHome native API on TCP port 6053 with Noise-protocol encryption. The device must be flashed with an api.encryption.key; the matching base64 PSK is passed to Dial.

The Client is long-lived. After a successful Dial it maintains the TCP session in the background, reconnecting with exponential backoff whenever the connection drops (device reboot, WiFi glitch, etc.). Commands issued while disconnected block until reconnection or the caller's context expires.

The entity schema is hardcoded for ratgdo boards — one cover entity named "door", one light, and the standard motion/obstruction/motor/button sensors. Other ESPHome devices will not work with this package.

Index

Constants

View Source
const Version = "0.4.0"

Version is the semantic version of this library and CLI. Bump with github.com/kevinburke/bump_version.

Variables

View Source
var (
	// ErrClosed is returned by commands issued after Close.
	ErrClosed = errors.New("ratgdo: client closed")
	// ErrNoEntity is returned when the firmware doesn't expose the entity
	// a command requires — i.e. this library is talking to something that
	// isn't a ratgdo after all.
	ErrNoEntity = errors.New("ratgdo: device does not expose the required entity")
)

Errors returned by Client methods.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a long-lived connection to a ratgdo device. Construct it with Dial; release it with Close.

func Dial

func Dial(ctx context.Context, addr, encryptionKey string, cfg *Config) (*Client, error)

Dial opens a connection to the ratgdo at addr (e.g. "ratgdo.local:6053") and performs the Noise handshake with the given base64 encryption key. If encryptionKey is empty, a plaintext session is used — only do that on a completely trusted network.

Pass nil for cfg to accept all defaults. If the initial session setup fails, Dial returns the error and no Client.

After Dial returns, the Client maintains the session in the background: on disconnect it reconnects with exponential backoff until Close.

func (*Client) Close

func (c *Client) Close() error

Close disconnects from the device, stops the background reconnect loop, and closes all Subscribe channels. It is safe to call Close more than once; subsequent calls return nil.

func (*Client) CloseDoor

func (c *Client) CloseDoor(ctx context.Context) error

CloseDoor sends a close command.

func (*Client) Connected

func (c *Client) Connected() bool

Connected reports whether the client currently has a live, authenticated session with the device.

func (*Client) DeviceInfo

func (c *Client) DeviceInfo(ctx context.Context) (*DeviceInfo, error)

DeviceInfo queries the device for its identity metadata. Unlike the other commands, DeviceInfo blocks until a response arrives or ctx expires.

func (*Client) OpenDoor

func (c *Client) OpenDoor(ctx context.Context) error

OpenDoor sends an open command to the opener. The call returns as soon as the command is on the wire; observe State or Subscribe to confirm the door actually moved.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) error

Ping probes the active API session. It blocks until the device responds, ctx expires, or the client disconnects.

func (*Client) QueryStatus

func (c *Client) QueryStatus(ctx context.Context) error

QueryStatus presses the "Query status" template button on the device.

func (*Client) SetDoorPosition

func (c *Client) SetDoorPosition(ctx context.Context, position float32) error

SetDoorPosition drives the door to the given fractional position (0 = fully closed, 1 = fully open). Ratgdo supports this natively.

func (*Client) State

func (c *Client) State() State

State returns a snapshot of the most recently observed device state. Safe to call at any time, including while disconnected (returns stale data — check State.LastSeenAt).

func (*Client) StopDoor

func (c *Client) StopDoor(ctx context.Context) error

StopDoor halts the door if it is currently moving.

func (*Client) Subscribe

func (c *Client) Subscribe() <-chan Event

Subscribe returns a channel that receives every state change plus connect/disconnect notifications. The channel is buffered; if a consumer falls more than the buffer behind, older events are dropped (logged). The channel closes when Close is called.

func (*Client) Sync

func (c *Client) Sync(ctx context.Context) error

Sync asks the device to re-query the opener for its current state. Useful if the ratgdo's state drifted from reality.

func (*Client) ToggleLight

func (c *Client) ToggleLight(ctx context.Context) error

ToggleLight flips the current light state.

func (*Client) TurnOffLight

func (c *Client) TurnOffLight(ctx context.Context) error

TurnOffLight turns the opener light off.

func (*Client) TurnOnLight

func (c *Client) TurnOnLight(ctx context.Context) error

TurnOnLight turns the opener light on.

func (*Client) WaitFor

func (c *Client) WaitFor(ctx context.Context, pred func(State) bool) error

WaitFor blocks until pred(c.State()) returns true, ctx expires, or the client is closed. pred is evaluated against the current state on every received event.

type Config

type Config struct {
	// ClientID is sent to the device as ClientInfo in the Hello handshake
	// and appears in the device's logs. Defaults to "ratgdo-go".
	ClientID string
	// Timeout bounds each network operation: the initial dial, the Noise
	// handshake, and individual wire writes. Defaults to 10s.
	Timeout time.Duration
	// Logger handles reconnect/error logging. Defaults to slog.Default().
	Logger *slog.Logger
}

Config holds the optional knobs for a Client. Pass nil to Dial for all defaults, or set only the fields you care about (zero values mean "use the default").

type DeviceInfo

type DeviceInfo struct {
	Name, Model, MACAddress, ESPHomeVersion, CompilationTime string
}

DeviceInfo holds static metadata about a ratgdo device.

type DoorOp

type DoorOp int

DoorOp is the door's motion state as reported by the opener.

const (
	// DoorUnknown is the zero value, used before the first state event arrives.
	DoorUnknown DoorOp = iota
	DoorClosed
	DoorOpen
	DoorOpening
	DoorClosing
	// DoorStopped means the door was halted mid-travel and is neither fully
	// open nor fully closed.
	DoorStopped
)

func (DoorOp) String

func (d DoorOp) String() string

type Event

type Event struct {
	At   time.Time
	Kind EventKind
	Prev State
	Curr State
}

Event is a change notification delivered on the channel returned by Client.Subscribe. For EventStateChange, Prev and Curr show the delta. For connection events, Prev and Curr are equal (both hold the most recent known state).

func (Event) DoorFinishedClosing

func (e Event) DoorFinishedClosing() bool

DoorFinishedClosing reports the door reaching the fully-closed state.

func (Event) DoorFinishedOpening

func (e Event) DoorFinishedOpening() bool

DoorFinishedOpening reports the door reaching the fully-open state.

func (Event) DoorStartedClosing

func (e Event) DoorStartedClosing() bool

DoorStartedClosing reports the door transitioning into the closing state.

func (Event) DoorStartedOpening

func (e Event) DoorStartedOpening() bool

DoorStartedOpening reports whether this event represents the door transitioning from closed or stopped into the opening state.

func (Event) OpeningsIncreased

func (e Event) OpeningsIncreased() bool

OpeningsIncreased reports whether the lifetime opening counter advanced in this event. Useful for detecting activity missed during a disconnect: a single EventConnected can report many openings if the gap was long.

type EventKind

type EventKind int

EventKind distinguishes state deltas from connection-lifecycle events.

const (
	// EventStateChange is emitted whenever any field of State differs from
	// the previous State.
	EventStateChange EventKind = iota
	// EventConnected is emitted after the initial Dial returns and after
	// every successful reconnection.
	EventConnected
	// EventDisconnected is emitted when the background session drops. A
	// reconnect attempt follows automatically unless Close was called.
	EventDisconnected
)

func (EventKind) String

func (k EventKind) String() string

type State

type State struct {
	Door     DoorOp
	Position float32 // 0 = fully closed, 1 = fully open
	Light    bool
	// Motion is true while the opener's motion sensor is active. It
	// typically auto-resets after a few seconds on the device.
	Motion      bool
	Obstruction bool
	// Openings is the lifetime count of door openings, persisted on-device
	// across reboots.
	Openings int
	// UpdatedAt is the time we last observed any state change. Zero before
	// the first state event.
	UpdatedAt time.Time
	// LastSeenAt is the time we last received any message from the device,
	// including pings and unchanged state updates. Zero until first message.
	LastSeenAt time.Time
}

State is a snapshot of the ratgdo's observable state. All fields reflect the last value received from the device.

Directories

Path Synopsis
cmd
ratgdo command
Command ratgdo is a small CLI around the ratgdo Go client.
Command ratgdo is a small CLI around the ratgdo Go client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL