Lightweight Bluetooth manager for Linux with: - D-Bus BlueZ client via nim-dbus - CLI binary (nbt) — list, scan, power, connect, etc. - Daemon (nimbt-daemon) — event loop + UNIX socket IPC - JSON, TOML, YAML config support - Nerd Font icons + plain/JSON output modes - Autoconnect engine for trusted devices - systemd user service unit - Arch Linux PKGBUILD
67 lines
1.8 KiB
Nim
67 lines
1.8 KiB
Nim
import os, posix, json, strutils, tables
|
|
import bt_types
|
|
|
|
const SocketPathEnv* = "NIMBT_SOCKET"
|
|
|
|
proc defaultSocketPath*: string =
|
|
let runtimeDir = getEnv("XDG_RUNTIME_DIR")
|
|
if runtimeDir.len > 0:
|
|
result = runtimeDir / "nimbt.sock"
|
|
else:
|
|
result = "/tmp/nimbt.sock"
|
|
|
|
proc getSocketPath*: string =
|
|
result = getEnv(SocketPathEnv)
|
|
if result.len == 0:
|
|
result = defaultSocketPath()
|
|
|
|
proc parseCmd*(line: string): SocketCmd =
|
|
let node = parseJson(line)
|
|
result.id = node["id"].getInt(0)
|
|
let cmdStr = node["cmd"].getStr("")
|
|
if cmdStr.len > 0:
|
|
result.cmd = parseEnum[SocketCmdKind](cmdStr, scList)
|
|
result.args = initTable[string, string]()
|
|
if node.hasKey("args") and node["args"].kind == JObject:
|
|
for k, v in node["args"].fields:
|
|
result.args[k] = v.getStr("")
|
|
|
|
proc cmdToJson*(cmd: SocketCmd): string =
|
|
var argsNode = newJObject()
|
|
for k, v in cmd.args:
|
|
argsNode[k] = %v
|
|
let node = %*{"id": cmd.id, "cmd": $cmd.cmd, "args": argsNode}
|
|
result = $node & "\n"
|
|
|
|
proc respToJson*(resp: SocketResp): string =
|
|
let node = %*{
|
|
"id": resp.id,
|
|
"ok": resp.ok,
|
|
"data": resp.data,
|
|
"error": resp.error,
|
|
"stream": resp.stream
|
|
}
|
|
result = $node & "\n"
|
|
|
|
proc sendResponse*(fd: cint; resp: SocketResp) =
|
|
let data = respToJson(resp)
|
|
discard write(fd, data.cstring, data.len)
|
|
|
|
proc recvCommand*(fd: cint; timeoutMs: int = 100): SocketCmd =
|
|
var buf: array[8192, char]
|
|
var line = ""
|
|
while true:
|
|
var pfd: TPollfd
|
|
pfd.fd = fd
|
|
pfd.events = POLLIN
|
|
pfd.revents = 0
|
|
let ret = poll(addr pfd, 1.Tnfds, timeoutMs.cint)
|
|
if ret <= 0: return
|
|
let n = read(fd, addr buf, buf.len)
|
|
if n <= 0: return
|
|
for i in 0..<n:
|
|
if buf[i] == '\n':
|
|
if line.len > 0:
|
|
return parseCmd(line)
|
|
else:
|
|
line.add(buf[i])
|