evangelotron — announce your Linux distribution to anyone within technical reach.
$ evangelotron --distro "$(< /etc/arch-release)" --dry-run
I use Arch btw
evangelotron: dry run selected localClaimer: The opinions expressed and implied by this tool absolutely ARE those of the creator and contributing community.
Evangelotron is a local-first Linux CLI. It detects or accepts a distribution name,
renders I use <distro> btw, and can announce that fact through local audio,
explicitly authorized Google Cast receivers, and a configured Home Assistant
event. The implementation is serious because the premise has already done
enough damage.
Python 3.11 or newer on Linux is required. Install from a checked-out source tree; this repository does not yet claim PyPI, AUR, or distro-package availability:
$ python -m pip install .The core package has no mandatory Python runtime dependencies. Direct Cast support is an optional extra:
$ python -m pip install '.[cast]'For development, install the editable package and tools, optionally including Cast support:
$ python -m pip install -e '.[dev]'
$ python -m pip install -e '.[cast,dev]'System capabilities are separate from Python dependencies. Local or Cast audio
needs espeak-ng; local playback additionally needs one of pw-play, paplay,
or aplay. Evangelotron probes these programs but never installs them automatically.
The source tree contains man/evangelotron.1. Python packaging does not portably install it as a system man page; later distro packaging may place it under the appropriate system man directory without requiring privileged pip behavior.
An unrelated project already uses btw as both a package and command name.
Evangelotron therefore installs its executable as evangelotron.
The intended command name for this project is still btw. Configure it in your
shell:
alias btw='evangelotron'Evangelotron does not modify shell configuration automatically. If another
btw command is already installed, rename or re-alias that command before
assigning btw to Evangelotron.
Autodetect the distro and use configured default sinks:
$ evangelotronUse a distro explicitly or preserve the Arch empty-marker joke:
$ evangelotron --distro Fedora --dry-run
I use Fedora btw
evangelotron: dry run selected local
$ evangelotron --distro "$(< /etc/arch-release)" --dry-run
I use Arch btw
evangelotron: dry run selected localSelect exactly the requested sink classes, or all classes enabled under
[announce]:
$ evangelotron --local
$ evangelotron --cast
$ evangelotron --home-assistant
$ evangelotron --local --cast
$ evangelotron --all--all never means every device visible on the LAN. --dry-run performs local
parsing and capability probes but never synthesizes, plays, discovers Cast,
contacts Home Assistant, starts a media server, or reserves cooldown.
Output controls are:
--json: one announcement result object on stdout;--quiet: suppress successful human output, not errors;--verbose: safe operational diagnostics on stderr.
Inspect local capabilities without announcing or networking:
$ evangelotron doctorExplicitly add bounded, non-mutating checks of the configured HA endpoint and configured Cast target resolution:
$ evangelotron doctor --networkDoctor warnings are nonfatal. Doctor returns 0 when no check fails and 1
when diagnostics complete with one or more failures. Usage errors remain 2,
unexpected internal errors remain 70, and Ctrl-C returns 130.
Announcement exit codes remain:
0: at least one selected sink succeeded and none failed, with skips allowed;2: CLI usage or configuration error;3: partial announcement success;4: no selected announcement sink succeeded;5: distro detection failure;6: cooldown prevented execution;70: unexpected internal error.
Configuration uses ${XDG_CONFIG_HOME:-~/.config}/btw/config.toml. A missing
file uses validated defaults and is not created automatically.
[announce]
local = true
cast = false
home_assistant = false
cooldown_seconds = 3
[speech]
provider = "auto"
voice = "en-gb"
# Piper execution is not implemented.
# [speech.piper]
# model = "/absolute/path/to/voice.onnx"
[local]
player = "auto"
[cast]
enabled = false
interrupt_active_media = false
discovery_timeout_seconds = 5
playback_timeout_seconds = 30
http_lifetime_seconds = 90
targets = []
known_hosts = []
[home_assistant]
enabled = false
url = "http://homeassistant.local:8123"
event_type = "btw_announce"
token_env = "BTW_HA_TOKEN"
allow_insecure_http = true
[message]
template = "I use {distro} btw"Unknown keys warn; malformed TOML, invalid types, invalid URLs, and unsafe
values fail before announcement side effects. The message template supports
only {distro}. The canonical terminal/JSON/HA text remains I use Arch btw;
speech providers receive the deliberately narrow I use Arch, by the way.
Cooldown state uses ${XDG_RUNTIME_DIR}/btw/ where available and otherwise
${XDG_CACHE_HOME:-~/.cache}/btw/. One invocation reserves once, and the lock
is released before synthesis or network work. --force bypasses only cooldown
timing; it does not bypass configuration, authorization, or security policy.
Install espeak-ng and at least one supported default-output player using your
system's normal administration process. Automatic player selection is:
pw-play(PipeWire);paplay(PulseAudio-compatible);aplay(ALSA).
No shell command is constructed from message data, and it does not change system volume or select a non-default device.
$ evangelotron --local --dry-run
$ evangelotron --distro Fedora --local
evangelotron: announced Fedora via localInstall the optional dependency, discover without playback, then manually copy an owned receiver's stable UUID into configuration:
$ python -m pip install '.[cast]'
$ evangelotron discover cast --timeout 5
UUID NAME MODEL ADDRESS
aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa Kitchen display Google Nest Mini 192.168.1.20[announce]
cast = true
[cast]
enabled = true
targets = ["aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"]UUIDs are preferred. Exact friendly names are accepted only when unique;
ambiguity fails instead of guessing. Discovery and known_hosts are resolution
hints, not authorization, and discovered inventories are not persisted.
A real Cast announcement performs bounded discovery only to resolve configured targets. One synthesized WAV is shared by local and Cast sinks. A short-lived stdlib HTTP server binds to a route-reachable address and ephemeral port, then serves only that WAV at a random unguessable path. Receivers must be able to reach the host; firewalls, client isolation, VLANs, VPNs, or model-specific behavior may prevent fetching.
Active or unknown media is skipped by default. Setting
interrupt_active_media = true explicitly permits replacement. Evangelotron never
changes Cast volume and uses no cloud TTS. Automated coverage is comprehensive,
but broad real-device compatibility has not been validated across receiver
models.
The opt-in live test controls only the explicitly supplied target:
$ BTW_LIVE_CAST_TEST=1 BTW_LIVE_CAST_TARGET=AUTHORIZED_UUID pytest -m live_castHome Assistant support sends one custom event to the configured endpoint. It does not assume a TTS provider, entity, or automation topology.
[announce]
home_assistant = true
[home_assistant]
enabled = true
url = "https://homeassistant.example.test:8123"
event_type = "btw_announce"
token_env = "BTW_HA_TOKEN"
allow_insecure_http = falseSet the referenced environment variable only in the invoking environment. Or
replace token_env with an absolute token_file path and restrict the file,
for example to mode 0600. Token values and file contents are never printed.
The schema-version 1 event payload is:
{
"schema_version": 1,
"source": "btw",
"message": "I use Arch btw",
"distro": "Arch",
"hostname": "workstation",
"direct_cast_targets": []
}Combined Cast/HA runs populate direct_cast_targets only with stable UUIDs of
receivers actually addressed by direct playback. HA-only runs retain [].
Example automation contract:
alias: BTW announcement
triggers:
- trigger: event
event_type: btw_announce
actions: [] # The Home Assistant owner decides what happens.evangelotron doctor --network uses authenticated GET /api/ and never fires the event
endpoint. TLS verification remains enabled. Plain HTTP works only when
explicitly allowed and always produces a doctor warning because bearer tokens
can be observed by parties able to inspect that network path.
Only configure devices and services that you own or are authorized to control.
Discovery is not authorization. --all is not uncontrolled broadcast mode.
Home Assistant traffic goes only to its configured endpoint, and direct Cast
playback goes only to configured UUIDs or exact unambiguous names. The project
has no telemetry, update checks, cloud TTS, automatic package installation,
automatic volume manipulation, daemon, or remote-control service.
See SECURITY.md for the implemented threat model, secret handling, temporary-server boundary, cleanup behavior, and reporting guidance.
Start locally:
$ evangelotron doctorOnly when you intend to contact configured local services, add:
$ evangelotron doctor --networkCommon findings:
espeak-ng executable not found: install it through the host distribution;- no local player: install PipeWire, PulseAudio-compatible, or ALSA tooling;
- Piper configured: Piper execution remains intentionally unimplemented;
- Cast extra missing: reinstall from source with
'.[cast]'; - Cast unresolved: confirm the configured UUID/name and discovery routing;
- Cast WAV fetch fails: inspect host firewall and client-isolation policy;
- HA token source missing: set the referenced variable or provide the file;
- HA plain HTTP warning: prefer locally trusted HTTPS where available;
- cooldown active: wait, or deliberately use
--forcefor that invocation.
evangelotron doctor is strictly local. evangelotron doctor --network is diagnostic and
non-mutating: it does not announce, synthesize, play, reserve cooldown, start a
Cast media server, change volume, or fire a Home Assistant event.
Install development tooling and run the same local gates as CI:
$ python -m pip install -e '.[cast,dev]'
$ python -m compileall -q src tests
$ ruff check .
$ pytest -m 'not live_cast'Normal tests use deterministic fakes or loopback-only fixtures. Never enable a live Cast test in automation. Keep core runtime dependencies empty, preserve explicit network authorization, and avoid adding deferred sinks or release machinery without an accepted design change.
The public package namespace, distro packaging, version 0.1.0, tag, and
release remain human/release-phase decisions. No release is claimed by this
development tree.
Evangelotron does not collect telemetry. It has better things to announce.
Copyright (C) 2026 James Wylie
Evangelotron is free software licensed under the GNU General Public License version 3
or, at your option, any later version. You can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
See LICENSE for the complete license text.