Commits
4f1f7f4
.github
dist
plasmoid
scripts
src
.gitignore 8 B
Cargo.lock 38.3 KiB
Cargo.toml 1.8 KiB
LICENSE.md 33.9 KiB
README.md 7.1 KiB

apollos-dbus

CI status audit status license install apollos-dbus (deb) install apollos-dbus (rpm)

Apollo’s Crib on the session bus.

A small daemon that connects to any number of Ankyrae and Keryxiae and keeps what they say as objects on the session bus – so a Plasma widget, a wallpaper, a panel script or anything else that speaks D-Bus can show a vision without each one holding its own broker connection, credentials and parser.

Nothing is polled. A Pythiae publishes on change, a Keryx on its tick; the daemon subscribes, asks for a fresh copy whenever it (re)connects, and keeps the last of everything on disk so a widget at login shows what was true at shutdown – marked stale – rather than nothing.

apollos-types is the source of truth for the shapes on the wire.

Install

TLS goes through the system OpenSSL, so building needs its headers and pkg-config (libssl-dev on Debian/Ubuntu, openssl-devel on Fedora, libopenssl-3-devel on openSUSE).

cargo build --release
install -Dm755 target/release/apollos-dbus ~/.local/bin/apollos-dbus
install -Dm644 dist/apollos-dbus.service ~/.config/systemd/user/apollos-dbus.service
sed -i "s|/usr/bin|$HOME/.local/bin|" ~/.config/systemd/user/apollos-dbus.service
install -Dm644 dist/config.example.toml ~/.config/apollos-dbus/config.toml   # then edit
chmod 600 ~/.config/apollos-dbus/config.toml
systemctl --user enable --now apollos-dbus

cargo deb / cargo generate-rpm package the binary, the unit, and a D-Bus activation file (dist/io.neiam.ApollosCrib.service) so the first widget to ask starts it.

Configure

~/.config/apollos-dbus/config.toml – see dist/config.example.toml. The short version:

[[ankyra]]
name = "home"
link = "apolloscrib://ankyra?host=..&user=..&pass=..&topic=.."

[[keryx]]
name = "desk"
id = 7
link = "apolloscrib://ankyra?host=..&user=..&pass=..&topic=.."

The link is the Pair a Client QR on cfg/ankyra/<id>; copy it out of the page. Any field next to a link overrides what the link says.

A Keryx is reached with any rabbit user belonging to the account that owns it – most easily an Ankyra’s, which is why a Keryx entry takes the same link. Its topics are apollos/keryx/<id>/...; this needs an apollos-crib with dotted, id-keyed Keryx topics (8a95d37 or later).

The client id defaults to apollos-dbus-<hostname>, and a second connection appends its source’s name. An Ankyra with auto-registration off refuses an id it has not been given – add it on the Ankyra’s page.

With tls = true the broker’s certificate is checked against the system’s roots and its host name. A broker signed by a private CA wants that CA in the system store, or SSL_CERT_FILE=/path/to/ca.pem in the unit’s environment.

The interface

Bus name io.neiam.ApollosCrib. Everything is a property, every change a PropertiesChanged, because that is what Plasma’s QML DBusProperties binds to. Structured values are JSON strings: a D-Bus struct reaches QML as an opaque QDBusArgument, a string reaches it as something JSON.parse reads.

/io/neiam/ApollosCrib — io.neiam.ApollosCrib1

Type
Board s Every query, keyed <type>-<id>: {name, type, id, query_id, source, updated, stale, data, meta, plus, points}
Points s Every query in a few words, flattened: [{key, name, type, text, color?, stale}]
Queries as The keys there are objects for
Sources s [{name, connected, error?}]
Connected b Whether any source is connected
Refresh() Ask every source for fresh data
RefreshQuery(s key) One query: a Keryx refreshes just it, an Ankyra its whole board
QueryUpdated(s key) signal News, as against the clock making something stale

Also org.freedesktop.DBus.ObjectManager, for GetManagedObjects.

/io/neiam/ApollosCrib/query/<type>_<id> — io.neiam.ApollosCrib1.Query

A key is <type>-<id>, and the id is not always a query’s: a Plani with a card per stop keys each <source>:<stop>, gtfs-1:2577, in the feed’s own stop ids. In the path the type’s dash is _ and any other character outside [A-Za-z0-9] is _ and its hex, so gtfs-12 is gtfs_12 and gtfs-1:2577 is gtfs_1_3a2577. Reading Board or Queries saves working that out.

Type
Key s gtfs-12
Type s gtfs, weather, …
Id s Everything after the type: 12, 1:2577
QueryId u The id as a query id; 0 where it is not one
Name s The query’s name, or its key if sent bare
Source s ankyra:<name> / keryx:<name>
Updated x Seconds since the epoch
Stale b Unheard from for over stale_after_secs (default 15 min)
Data s The condensed reading, JSON, in its apollos-types shape
Meta s The query’s meta, JSON
Plus s The Plus reading, JSON, null where none
Points as The reading in a few words

Points are for a consumer with room for a sentence and no business knowing what a GBFS dock is: 14 Heath St · 08:03 08:20, Union Sq: 5 bikes (2 e) · 7 docks, Home: 12°C light rain. Transit, bikeshare, weather, tides, air quality, sun and moon, and calendar have them; anything else has none, and Data is still there.

From QML

import org.kde.plasma.workspace.dbus as DBus

DBus.Properties {
    id: crib
    busType: DBus.BusType.Session
    service: "io.neiam.ApollosCrib"
    path: "/io/neiam/ApollosCrib"
    iface: "io.neiam.ApollosCrib1"
}

Text { text: JSON.parse(crib.properties.Points || "[]").map(p => p.text).join("\n") }

plasmoid/ is the worked example – a widget with a heading per query, dimmed when stale, picking its queries from the bus:

kpackagetool6 --type Plasma/Applet --install plasmoid    # --upgrade thereafter

From a shell

busctl --user get-property io.neiam.ApollosCrib /io/neiam/ApollosCrib io.neiam.ApollosCrib1 Points
busctl --user call io.neiam.ApollosCrib /io/neiam/ApollosCrib io.neiam.ApollosCrib1 Refresh

Not here yet

  • The uplink. A Plani follows a client’s position, and the phone reports one on <topic>.up.; a desktop does not.
  • The Ankyra’s image (<topic>.img), which is for e-paper.
  • Keryx raw and per-item topics: only .meta, the condensed reading, is subscribed to. The rest is most of the traffic and none of the summary.