// The bluetooth module's tools (novox/hq research 027/02, 026/05): the controller, the devices with // their state and battery, scanning, and pairing, connecting, trusting and forgetting a device. A Go // bundle the node's runtime launches over stdio (ADR 0188, ADR 0193); it runs as the operator // account, and speaks to bluez through bluetoothctl. package main import ( "fmt" "os" stdio "git.novox.be/novox/mesh-sdk/go" ) var providedBy = map[string]string{ "bluetoothctl": "the bluez-utils package, which this module installs", } func main() { if err := stdio.Serve("", tools()); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } var addressArg = map[string]any{"type": "string", "description": "the device's address, such as 80:99:E7:C2:29:DA, as bluetooth_devices answers it"} func withAddress(f func(string) (any, error)) func(map[string]any) (any, error) { return func(args map[string]any) (any, error) { a, err := addressOf(args) if err != nil { return nil, err } return f(a) } } func tools() []stdio.Tool { return []stdio.Tool{ { Name: "bluetooth_controller", Description: "The machine's Bluetooth controller: address, name, powered, discoverable, pairable, discovering. (r)", Input: map[string]any{}, Run: func(map[string]any) (any, error) { return Controller() }, }, { Name: "bluetooth_power", Description: "Whether the controller is powered; with on, switch it on or off. (r/a)", Input: map[string]any{"on": map[string]any{"type": "boolean", "description": "power the controller on (true) or off (false)"}}, Run: func(args map[string]any) (any, error) { if _, given := args["on"]; !given { c, err := Controller() if err != nil { return nil, err } return map[string]any{"powered": c.Powered}, nil } on, err := optFlag(args, "on", true) if err != nil { return nil, err } return Power(on) }, }, { Name: "bluetooth_devices", Description: "The devices bluez knows: every one, or only the paired, connected or trusted. Each with its " + "name, kind, paired, bonded, trusted, blocked, connected, and its battery where the device reports it. (r)", Input: map[string]any{"which": map[string]any{"type": "string", "enum": []string{"all", "paired", "connected", "trusted"}, "description": "which devices (default all)"}}, Run: func(args map[string]any) (any, error) { which, err := optText(args, "which", "all") if err != nil { return nil, err } return Devices(which) }, }, { Name: "bluetooth_scan", Description: "Discover devices nearby for a few seconds (default 8, at most 15), and answer the ones not " + "paired, with their signal strength. (r)", Input: map[string]any{"seconds": map[string]any{"type": "integer", "description": "how long to scan (default 8, at most 15)"}}, Run: func(args map[string]any) (any, error) { s, err := optWhole(args, "seconds", 8, 1, 15) if err != nil { return nil, err } return Scan(s) }, }, { Name: "bluetooth_connect", Description: "Connect a paired device, such as headphones. Answers its state afterwards. (a)", Input: map[string]any{"address": addressArg}, Run: withAddress(func(a string) (any, error) { return Act("connect", a) }), }, { Name: "bluetooth_disconnect", Description: "Disconnect a device. (a)", Input: map[string]any{"address": addressArg}, Run: withAddress(func(a string) (any, error) { return Act("disconnect", a) }), }, { Name: "bluetooth_trust", Description: "Trust a device, so it may connect by itself; or with trusted false, stop trusting it. (a)", Input: map[string]any{"address": addressArg, "trusted": map[string]any{"type": "boolean", "description": "trust (default) or untrust"}}, Run: func(args map[string]any) (any, error) { a, err := addressOf(args) if err != nil { return nil, err } trusted, err := optFlag(args, "trusted", true) if err != nil { return nil, err } if trusted { return Act("trust", a) } return Act("untrust", a) }, }, { Name: "bluetooth_pair", Description: "Pair a device found by a scan, and trust it. Works for a device that needs no code to be " + "confirmed, such as headphones; one that shows a code is paired from the desktop. (a)", Input: map[string]any{"address": addressArg}, Run: withAddress(func(a string) (any, error) { return Pair(a) }), }, { Name: "bluetooth_remove", Description: "Forget a device: unpair it and drop what bluez knows of it. (a)", Input: map[string]any{"address": addressArg}, Run: withAddress(func(a string) (any, error) { return Act("remove", a) }), }, } }