knxrpc

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2025 License: Apache-2.0 Imports: 33 Imported by: 1

README

knxrpc

Go Reference Actions Status Actions Status

Documentation

Description

knxrpc provides a ConnectRPC handler and server for RPCs interacting with a KNX bus in smart homes.

You can either integrate it as a http.Handler in your own code or run the included server which has swagger and metrics support.

You can find the protobuf-spec in groupaddressservice.proto.

You can access Swagger UI (if enabled via config) via: http://localhost:8080/swagger.

knxrpc API client using golang

You can use the generated golang code and helper funcs to build your own API client.

Take this as an example:

package main

import (
    "github.com/choopm/knxrpc"
    v1 "github.com/choopm/knxrpc/knx/groupaddress/v1"

    "github.com/vapourismo/knx-go/knx/dpt"
    // ...
)

func main() {
    // build ctx
    ctx, cancel := context.WithTimeout(context.Background(), time.Second() * 3)
    defer cancel()

    // construct knxrpc client
    client, err := knxrpc.NewClient(knxrpc.ClientConfig{
        Host: "127.0.0.1",
        Port: 8080,
    })
    if err != nil {
        panic(fmt.Errorf("knxrpc: %s", err))
    }

    // publish a message
    _, err := client.Publish(ctx, connect.NewRequest(&v1.PublishRequest{
        GroupAddress: "1/2/3",
        Data:         dpt.DPT_1002(true).Pack(),
        Event:        v1.Event_EVENT_WRITE,
    }))
    if err != nil {
        panic(fmt.Errorf("unable to send: %s", err))
    }

    // connect stream to receive bus messages for specific group addresses
    stream, err := client.Subscribe(ctx, connect.NewRequest(&v1.SubscribeRequest{
        GroupAddresses: []string{"1/2/3", "4/5/6"},
        Event:          v1.Event_EVENT_UNSPECIFIED,
    }))
    if err != nil {
        panic(fmt.Errorf("stream open: %s", err))
    }
    defer stream.Close()

    // start receiver loop
    for stream.Receive() {
        res := stream.Msg()
        // TODO handle res message here
        fmt.Println(res.GroupAddress)
        fmt.Println(res.Event)
        fmt.Println(res.Data)
        // ...
    }
    if connectErr := new(connect.Error); errors.As(err, &connectErr) &&
        !errors.Is(connectErr, context.Canceled) &&
        !errors.Is(connectErr, context.DeadlineExceeded) {
        panic(fmt.Errorf("stream closed: %s", stream.Err()))
    }
}
knxrpc binary - server mode

Starting the container will run the knxrpc binary in server mode using the argument server.

This will provide a ConnectRPC server to handle the RPCs defined at groupaddressservice.proto.

You will have to configure your KNX gatewayHost and port in the YAML config. You can also override the defaults using environment variables whiceh are derived from the YAML path such as:

  • KNXRPC_KNX_GATWAYHOST=192.168.1.2
  • KNXRPC_RPC_AUTH_ENABLED=true
  • KNXRPC_RPC_AUTH_SECRETKEY=password

Note the prefix KNXRPC when setting environment variables from YAML paths.

If enabled and configured in knxrpc.yaml, you will be able to use the SwaggerUI for testing RPCs.

When deploying to public or production, make sure to use TLS and authorization as otherwise you would be allowing public access to the KNX bus.

knxrpc binary - client publish/subscribe

You can also run the subcommands subscribe or publish to directly interact with any running RPC server.

This is useful when you are already running a knxrpc server. The client commands will use the knxrpc: section from YAML for connecting to the knxrpc server.

publishing
# send the data '\x01' (hex string) to group address 0/4/0
/usr/bin/knxrpc publish 0/4/0 01

# send the hex-string of 'hello world' to group address 1/2/3
/usr/bin/knxrpc publish 1/2/3 68656c6c6f20776f726c64

# send a read request
/usr/bin/knxrpc publish 0/5/6

# send a response message
/usr/bin/knxrpc publish --event response 0/5/6 fffd
subscribing
# subscribe to all bus messages by not providing a filter
/usr/bin/knxrpc subscribe

# subscribe to all response messages (which are send as a result to previous read requests)
/usr/bin/knxrpc subscribe --event response

# subscribe to specific group address(es)
/usr/bin/knxrpc subscribe 0/5/6 0/4/0 1/2/3
JSON client
Publishing a write event

This example sends BASE64-encoded hex data \x01 to the groupAddress 0/5/6.

curl -X 'POST' \
  'http://localhost:8080/knx.groupaddress.v1.GroupAddressService/Publish' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer CHANGEME' \
  -H 'Content-Type: application/json' \
  -d '{
  "groupAddress": "0/5/6",
  "event": "EVENT_WRITE",
  "data": "AQo="
}'
Publishing a read event

In this example a read-event is send to the groupAddress 0/5/6 which will trigger an asynchronous response to be read using the Subscribe-API.

curl -X 'POST' \
  'http://localhost:8080/knx.groupaddress.v1.GroupAddressService/Publish' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer CHANGEME' \
  -H 'Content-Type: application/json' \
  -d '{
  "groupAddress": "0/5/6",
  "event": "EVENT_READ"
}'
Subscribing with JSON clients

Subscription is implemented as a streming RPC and therefore an actual ConnectRPC client is required.

If you really require to use a JSON client for receiving messages, you might use wrapped SubscribeUnary API. Which internally creates the subscription stream and collects all event message for a duration of for.

# receive any messages on the bus for 10s
curl -X 'POST' \
  'http://localhost:8080/knx.groupaddress.v1.GroupAddressService/SubscribeUnary' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer CHANGEME' \
  -H 'Content-Type: application/json' \
  -d '{
  "subscribeRequest": {
    "groupAddresses": [],
    "event": "EVENT_UNSPECIFIED"
  },
  "for": "10s"
}'

If you omit the value of for, the first event message received will terminate the stream and your client will see the result immediately.

# receive the first message on the bus (missing for)
curl -X 'POST' \
  'http://localhost:8080/knx.groupaddress.v1.GroupAddressService/SubscribeUnary' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer CHANGEME' \
  -H 'Content-Type: application/json' \
  -d '{
  "subscribeRequest": {
    "groupAddresses": []
  }
}'

Development

Dev container

Open this project in Visual Studio Code and select to reopen it inside a dev container.

If you experience any issues, make sure your IDE supports dev containers: https://code.visualstudio.com/docs/devcontainers/containers

Tasks

This project uses task.

Run task --list to list all available tasks.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidAuthCredentials = errors.New("invalid auth credentials")
)

Functions

func NewAuthInterceptor

func NewAuthInterceptor(config AuthConfig) *headerInterceptor

NewAuthInterceptor returns a [headerInterceptor] to add authentication

func NewClient

NewClient returns a fresh GroupAddressServiceClient from config

Types

type AuthConfig

type AuthConfig struct {
	// Enabled whether to require and check authentication
	Enabled bool `mapstructure:"enabled" default:"false"`

	// Header is the header to fetch the key, required if [Enabled]
	Header string `mapstructure:"header" default:"Authorization"`

	// Scheme defines the auth scheme which is stripped from the header value
	Scheme string `mapstructure:"scheme" default:"Bearer"`

	// SecretKey is the key to compare the Header value with, required if [Enabled]
	SecretKey string `mapstructure:"secretKey" default:""`
}

AuthConfig holds the auth configuration

func (*AuthConfig) Validate

func (c *AuthConfig) Validate() error

Validate validates the AuthConfig

type ClientConfig

type ClientConfig struct {
	// Host is the knxrpc host to connect to
	Host string `mapstructure:"host"`

	// Port is the knxrpc host to connect to
	Port int `mapstructure:"port"`

	// Auth config to use
	Auth AuthConfig `mapstructure:"auth"`

	// UseTLS whether to connect using TLS
	UseTLS bool `mapstructure:"useTLS"`

	// InsecureTLS whether to use insecureSkipVerify
	InsecureTLS bool `mapstructure:"insecureTLS"`
}

ClientConfig holds the knxrpc client config

func (*ClientConfig) Validate

func (c *ClientConfig) Validate() error

Validate validates the ClientConfig

type Config

type Config struct {
	// Log stores logging config
	Log loggingfx.Config `mapstructure:"log"`

	// KNX is the KNX bus config, required
	KNX KNXConfig `mapstructure:"knx"`

	// RPC is the rpc config, required
	RPC RPCConfig `mapstructure:"rpc"`

	// Client is the client config to test the server, optional
	Client ClientConfig `mapstructure:"knxrpc"`
}

Config holds the required config for New

func (*Config) Validate

func (c *Config) Validate() error

Validate validates the Config

type KNXConfig

type KNXConfig struct {
	// GatwewayHost is the Host or IP address of a KNX gateway, required
	GatwewayHost string `mapstructure:"gatewayHost"`

	// GatwewayPort is the port to use when communicating, defaults to 3671
	GatwewayPort int `mapstructure:"gatewayPort" default:"3671"`

	// Timeout is the default timeout for any bus activity or operation
	Timeout time.Duration `mapstructure:"timeout" default:"10s"`

	// InactivityTimeout is the timeout after which the servers errors if no bus activity was seen
	InactivityTimeout time.Duration `mapstructure:"inactivityTimeout" default:"5m"`

	// SendLocalAddress sends the local address when establishing a tunnel (breaks NAT)
	SendLocalAddress bool `mapstructure:"sendLocalAddress" default:"false"`

	// UseTCP establishes the tunnel using tcp instead of udp
	UseTCP bool `mapstructure:"useTCP" default:"false"`
}

KNXConfig holds the KNX bus config

func (*KNXConfig) Validate

func (c *KNXConfig) Validate() error

Validate validates the KNXConfig

type MetricsConfig

type MetricsConfig struct {
	// Enabled whether to serve swaggerui
	Enabled bool `mapstructure:"enabled" default:"false"`

	// Path to serve mectris on
	Path string `mapstructure:"path" default:"/metrics"`

	// Auth config to use
	Auth AuthConfig `mapstructure:"auth"`
}

MetricsConfig holds the metrics configuration

func (*MetricsConfig) Validate

func (c *MetricsConfig) Validate() error

Validate validates the MetricsConfig

type RPCConfig

type RPCConfig struct {
	// Auth config to use
	Auth AuthConfig `mapstructure:"auth"`

	// Webserver config to use
	Webserver WebserverConfig `mapstructure:"webserver"`
}

RPCConfig holds the RPC config

func (*RPCConfig) Validate

func (c *RPCConfig) Validate() error

Validate validates the RPCConfig

type Server

type Server struct {
	http.Handler
	v1Connect.UnimplementedGroupAddressServiceHandler
	// contains filtered or unexported fields
}

Server implements RPCs using a http.Handler.

func New

func New(config *Config, logger *zerolog.Logger) (*Server, error)

New returns a new *KNXConnect or error Use [Start] to connect the KNX tunnel and start dispatching messages.

func (*Server) Publish

Publish implements knx.groupaddressservice.v1.Publish

func (*Server) Start

func (s *Server) Start(ctx context.Context) error

Start will connect to the KNX bus and start message handling or error. You may cancel ctx any time to close the tunnel and stop message handling.

func (*Server) Subscribe

Subscribe implements knx.groupaddressservice.v1.Subscribe

func (*Server) SubscribeUnary

SubscribeUnary implements knx.groupaddressservice.v1.SubscribeUnary

type SwaggerConfig

type SwaggerConfig struct {
	// Enabled whether to serve swaggerui
	Enabled bool `mapstructure:"enabled" default:"false"`

	// Path to serve swagger on
	Path string `mapstructure:"path" default:"/swagger"`

	// RootRedirect redirects / to <Path>/ if enabled
	RootRedirect bool `mapstructure:"rootRedirect" default:"false"`
}

SwaggerConfig holds the swagger configuration

func (*SwaggerConfig) Validate

func (c *SwaggerConfig) Validate() error

Validate validates the SwaggerConfig

type WebserverConfig

type WebserverConfig struct {
	// Enabled whether to start a http listener for the rpc server
	Enabled bool `mapstructure:"enabled" default:"false"`

	// Host is the listening host to use when starting a server
	Host string `mapstructure:"host" default:"127.0.0.1"`

	// Port is the listening port to use when starting a server
	Port int `mapstructure:"port" default:"8080"`

	// LogRequests whether to log requests
	LogRequests bool `mapstructure:"logRequests"`

	// Swagger config to use
	Swagger SwaggerConfig `mapstructure:"swagger"`

	// Metrics config to use
	Metrics MetricsConfig `mapstructure:"metrics"`
}

WebserverConfig holds the webserver config

func (*WebserverConfig) Validate

func (c *WebserverConfig) Validate() error

Validate validates the HTTPConfig

Directories

Path Synopsis
cmd
knxrpc command
knx

Jump to

Keyboard shortcuts

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