Breeze Core packages
Signed packages for Breeze Core — self-hosted, LAN-first control for Midea air conditioners. v@VERSION@
Documentation lives in the wiki — installing, pairing, the REST API, and how to expose it safely. This page is just the packages.
Debian family
Debian · Ubuntu · Mint · Pop!_OS · Raspberry Pi OS · Devuan —apt
# the key gets its own keyring, referenced by the sources entry
curl -fsSL https://bolero.salataputarica.hr.eu.org/breeze-core.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/breeze-core.gpg
echo "deb [signed-by=/usr/share/keyrings/breeze-core.gpg] https://bolero.salataputarica.hr.eu.org/deb stable main" \
| sudo tee /etc/apt/sources.list.d/breeze-core.list
sudo apt update && sudo apt install breeze-core
Fedora / RHEL family
Fedora · RHEL · AlmaLinux · Rocky · CentOS Stream —dnf
# import the key first: dnf 5 will not fetch it from gpgkey= on its own
sudo rpm --import https://bolero.salataputarica.hr.eu.org/breeze-core.asc
sudo tee /etc/yum.repos.d/breeze-core.repo <<'EOF'
[breeze-core]
name=Breeze Core
baseurl=https://bolero.salataputarica.hr.eu.org/rpm/$basearch/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://bolero.salataputarica.hr.eu.org/breeze-core.asc
EOF
sudo dnf install breeze-core
.repo file works on both.
On dnf 4 (RHEL 8/9, Fedora ≤ 40) the key would be imported interactively on first install —
the rpm --import above just avoids the prompt. On dnf 5 (Fedora 41+, RHEL 10) that
pre-import is required: dnf 5 no longer pulls the key from gpgkey= itself.
Prefer dnf config-manager? The verb changed too — dnf 5 wants
dnf config-manager addrepo --from-repofile=<url> where dnf 4 used --add-repo.openSUSE / SLE
Leap · Tumbleweed · SLES / SLED —zypper
sudo rpm --import https://bolero.salataputarica.hr.eu.org/breeze-core.asc
# swap x86_64 for aarch64 on ARM
sudo zypper addrepo --refresh --gpgcheck \
https://bolero.salataputarica.hr.eu.org/rpm/x86_64/ breeze-core
sudo zypper --gpg-auto-import-keys refresh
sudo zypper install breeze-core
The same rpms as the Fedora/RHEL section — zypper just adds and refreshes repos its own way.
--gpg-auto-import-keys accepts the repo signature without prompting.
Arch family
Arch · Manjaro · Artix · EndeavourOS —pacman
# fetch once, then add + locally sign the key by its fingerprint
curl -fsSL -o /tmp/breeze-core.asc https://bolero.salataputarica.hr.eu.org/breeze-core.asc
sudo pacman-key --add /tmp/breeze-core.asc
sudo pacman-key --lsign-key "$(gpg --show-keys --with-colons /tmp/breeze-core.asc \
| awk -F: '/^fpr/{print $10; exit}')"
sudo tee -a /etc/pacman.conf <<'EOF'
[breeze-core]
SigLevel = Required
Server = https://bolero.salataputarica.hr.eu.org/arch/$arch
EOF
sudo pacman -Sy breeze-core
Alpine
Alpine Linux —apk · x86_64 · aarch64 · riscv64 · run as root (Alpine ships doas, not sudo)
# as root — prefix with `doas` if you use it
apk add --no-cache curl
curl -fsSL -o /etc/apk/keys/breeze-core@bolero.rsa.pub \
https://bolero.salataputarica.hr.eu.org/alpine/breeze-core@bolero.rsa.pub
echo "https://bolero.salataputarica.hr.eu.org/alpine" >> /etc/apk/repositories
apk update && apk add breeze-core
The index is RSA-signed with the key above, so no --allow-untrusted is needed.
OpenWrt
x86_64 · aarch64_generic · aarch64_cortex-a53 · aarch64_cortex-a72 —opkg · run as root
wget -q -O /tmp/breeze.pub https://bolero.salataputarica.hr.eu.org/openwrt/breeze-core-usign.pub cp /tmp/breeze.pub "/etc/opkg/keys/$(usign -F -p /tmp/breeze.pub)" echo "src/gz breeze_core https://bolero.salataputarica.hr.eu.org/openwrt/$(. /etc/openwrt_release; echo $DISTRIB_ARCH)" \ >> /etc/opkg/customfeeds.conf opkg update && opkg install breeze-core
Signed with usign, the same signer OpenWrt itself uses, so opkg update
verifies the feed. Needs OpenWrt 23.05+. Sizes are per architecture — you only ever download
your own: about 24 MB to fetch, ~51–63 MB installed (a private Python runtime and every
dependency, so there's nothing else to install). That means x86 boxes, NAS/RPi-class devices, or a
router with extroot.
.ipk: the packages wrap a frozen Python bundle, PyInstaller
cannot cross-compile, and 24 MB does not fit 8–16 MB of flash anyway.
The full account.riscv64 ships as a musl build — install it
from the Alpine section above. Expect it to land last of any architecture and to
lag this page: nothing on PyPI is prebuilt for it, so every dependency compiles from source under
emulation. No glibc riscv64 build, and no OpenWrt riscv64 .ipk —
why.FreeBSD
amd64 — a real, RSA-signedpkg repository · verified on FreeBSD 15.1
sudo mkdir -p /usr/local/etc/pkg/keys /usr/local/etc/pkg/repos
sudo fetch -o /usr/local/etc/pkg/keys/breeze-core.pub \
https://bolero.salataputarica.hr.eu.org/freebsd/breeze-freebsd-repo.rsa.pub
sudo tee /usr/local/etc/pkg/repos/breeze-core.conf <<'EOF'
breeze-core: {
url: "https://bolero.salataputarica.hr.eu.org/freebsd",
signature_type: "pubkey",
pubkey: "/usr/local/etc/pkg/keys/breeze-core.pub",
enabled: yes
}
EOF
sudo pkg update && sudo pkg install breeze-core
NetBSD
amd64/x86_64 — apkgin binary repository · verified on NetBSD 11.0
# needs python312 from pkgsrc (pulled in as a dependency)
echo "https://bolero.salataputarica.hr.eu.org/netbsd/All" \
| su root -c 'tee -a /usr/pkg/etc/pkgin/repositories.conf'
su root -c 'pkgin update && pkgin -y install breeze-core'
OpenBSD
amd64 — installs from source into a private virtualenv · verified on OpenBSD 7.9# python3.13 is already in base on 7.9; rust builds pydantic-core
doas pkg_add rust
ftp -o breeze-core-@VERSION@.tar.gz \
https://bolero.salataputarica.hr.eu.org/src/breeze-core-@VERSION@.tar.gz
tar xzf breeze-core-@VERSION@.tar.gz && cd breeze-core
doas sh packaging/bsd/install.sh
/usr/local/breeze-core and installs an rcctl-managed service. The Linux
packages on this page are Linux ELF binaries and cannot run on a BSD kernel; FreeBSD and NetBSD get
native packages because their tooling (pkg / pkg_create) makes that
practical. A ports submission is welcome — see
packaging/bsd/.OPNsense
amd64 — theos-breeze-core plugin, adds Services → Breeze Core to the GUI
# from a shell on the firewall, as root
fetch https://bolero.salataputarica.hr.eu.org/opnsense/os-breeze-core-latest.pkg
pkg add os-breeze-core-latest.pkg
Then Services → Breeze Core to set the listen address and enable it,
breeze-core pair to pair the air conditioners, and
breeze-core approve to admit a phone or browser.
FreeBSD:14:amd64 with python311 and neither rust nor
pip, so the whole Python runtime is vendored inside the plugin and
nothing is ever compiled on the firewall. Checksums:
/opnsense/.Windows
Windows 10 · 11 · Server — an installer, not a repo · runs as a serviceUnlike the Linux packages, this one does not bundle its own
Python: the installer detects Python 3.11+ and builds a private virtualenv, then registers a hardened
BreezeCore service (via NSSM, running as LOCAL SERVICE, with a LAN-only
firewall rule and a locked-down %ProgramData%reeze-core). An optional component sets up
Caddy as a reverse proxy.
# download, then double-click — or install silently: Breeze-Core-Setup.exe /S # verify it first (PowerShell): Get-FileHash .\Breeze-Core-Setup.exe -Algorithm SHA256
⬇ Breeze-Core-Setup.exe · sha256 · all versions
Breeze.BreezeCore live under
/winget/. The package isn't in the public
winget-pkgs repository yet, so install it from
the manifest — which Windows gates behind an admin setting:
# once, in an ADMIN PowerShell — winget refuses local manifests otherwise: winget settings --enable LocalManifestFiles # then, as yourself: mkdir breeze-core-@VERSION@; cd breeze-core-@VERSION@ 'Breeze.BreezeCore.yaml','Breeze.BreezeCore.installer.yaml','Breeze.BreezeCore.locale.en-US.yaml' | % { iwr "https://bolero.salataputarica.hr.eu.org/winget/@VERSION@/$_" -OutFile $_ } winget install --manifest .winget then downloads the installer from GitHub and checks it against the SHA256 in the manifest. A private winget source isn't an option here — sources speak a REST API, and this host deliberately serves static files only.
Breeze for Android
the phone client — not the server · sideload APK · Android 7+Everything above installs the server. This is the app that talks to it: one AC per screen, home-screen widgets, server-side schedules, and an Android Auto surface. It's signed with the project's own release key and distributed as an APK — there's no Play listing, so your phone will ask you to allow installs from this source.
# download to your phone, or push it over adb adb install breeze-latest.apk # verify what you downloaded first (recommended): sha256sum -c breeze-latest.apk.sha256
⬇ breeze-latest.apk · sha256 · all versions — or grab it from GitHub releases.
After installing
three steps on every platform — then find your init system below# 1. find your ACs and write the config breeze-core pair # 2. set this machine's LAN IP (or keep 127.0.0.1 behind a reverse proxy) # Linux /etc/breeze-core/breeze-core.env → BREEZE_HOST=... # FreeBSD /usr/local/etc/breeze-core/breeze-core.env # NetBSD /usr/pkg/etc/breeze-core/breeze-core.env # OpenBSD /etc/breeze-core/breeze-core.env # 3. enable + start the service — pick your init system:
systemd
Debian · Ubuntu · Fedora · RHEL · SUSE · Archsudo systemctl enable --now breeze-core
sudo systemctl status breeze-core
journalctl -u breeze-core -f # follow the log
OpenRC
Alpine · Artix (openrc) · Gentoo · Devuanrc-update add breeze-core default rc-service breeze-core start rc-service breeze-core status
runit
Void · Artix (runit)# template ships at deploy/init/runit-run sudo mkdir -p /etc/sv/breeze-core sudo cp deploy/init/runit-run /etc/sv/breeze-core/run sudo chmod +x /etc/sv/breeze-core/run sudo ln -s /etc/sv/breeze-core /var/service/ # /run/runit/service on Void sudo sv status breeze-core
s6 / s6-rc
Artix (s6) · Obarun# template ships at deploy/init/s6-run
sudo mkdir -p /etc/s6/sv/breeze-core
sudo cp deploy/init/s6-run /etc/s6/sv/breeze-core/run
sudo chmod +x /etc/s6/sv/breeze-core/run
sudo s6-rc-bundle-update add default breeze-core
sudo s6-rc -u change breeze-core
SysV init
Devuan (sysvinit) · Slackware · older LTS releases# template ships at deploy/init/sysv-breeze-core sudo cp deploy/init/sysv-breeze-core /etc/init.d/breeze-core sudo chmod +x /etc/init.d/breeze-core sudo update-rc.d breeze-core defaults # RHEL-ish: chkconfig --add breeze-core sudo service breeze-core start sudo service breeze-core status
procd
OpenWrt/etc/init.d/breeze-core enable
/etc/init.d/breeze-core start
logread -e breeze-core # follow the log
rc.d — FreeBSD
FreeBSD 14 · 15sudo sysrc breeze_core_enable=YES sudo service breeze_core start sudo service breeze_core status tail -f /var/log/breeze_core.log
rc.d — NetBSD
NetBSD 10 · 11echo 'breeze_core=YES' | su root -c 'tee -a /etc/rc.conf' su root -c 'service breeze_core start' su root -c 'service breeze_core status' tail -f /var/log/breeze_core.log
rcctl — OpenBSD
OpenBSD 7.xdoas rcctl enable breeze_core
doas rcctl start breeze_core
doas rcctl check breeze_core
# host/port live in /etc/breeze-core/breeze-core.env
Windows service
Windows 10 · 11 · Server — installed by the .exe (NSSM)Get-Service BreezeCore
Restart-Service BreezeCore
# config lives in %ProgramData%\breeze-core
Then open http://<BREEZE_HOST>:8420, enter the API key
from your config.json, and pair. The service runs as an unprivileged breeze user, and
uninstalling keeps your config and device tokens. The runit/s6/SysV templates live in
deploy/init/.
Proof of concept
s390x · ppc64le · Termux · OpenWrt MIPS · Debian mips64el — frozen at v3.0.5, for developersThese are not products. They exist so somebody porting Breeze Core to an unusual machine starts from a working build and a list of the walls, instead of from scratch. Frozen at 3.0.5, outside the signed repositories, no support. Every one was verified by running it on the target under emulation — with one clearly-labelled exception, the Octeon wheelhouse, which has never run.
| Termux (Android, no root) | termux-aarch64 · termux-x86_64 | a frozen bundle for Termux's own interpreter path |
| s390x | linux-musl-s390x | mainframes need cooling too |
| ppc64le | linux-musl-ppc64le | same recipe as s390x |
| OpenWrt MIPS | mips_24kc (BE) · mipsel_24kc (LE) | wheelhouses, built against OpenWrt's SDK — the one that matters |
| Debian mips64el | linux-glibc-mips64el | glibc wheelhouse; Loongson 3, Cavium on Debian. Not for routers |
| Octeon ⚠ | mips64_octeonplus | UNVERIFIED — compiles and inspects correctly, never executed |
# bundles: download, extract, run curl -fsSLO https://bolero.salataputarica.hr.eu.org/poc/breeze-core-3.0.5-linux-musl-s390x.tar.gz tar -xzf breeze-core-3.0.5-linux-musl-s390x.tar.gz ./breeze-core-3.0.5-linux-musl-s390x/breeze-core version # wheelhouses: pip them into an extroot. --only-binary is not optional -- # without it a tag mismatch quietly starts compiling Rust on a router. opkg update && opkg install python3 python3-pip python3-cryptodome curl -fsSLO https://bolero.salataputarica.hr.eu.org/poc/breeze-core-3.0.5-openwrt-mips_24kc-wheelhouse.tar.gz tar -xzf breeze-core-3.0.5-openwrt-mips_24kc-wheelhouse.tar.gz cd breeze-core-3.0.5-openwrt-mips_24kc-wheelhouse pip install --find-links . --only-binary pydantic-core pydantic-core pip install fastapi uvicorn msmart-ng brotli-asgi
uname -m says
mips for both endiannesses, so the filenames are identical and the contents are not.
It fails safely — the wrong one raises ImportError rather than executing byte-swapped
code — but keep them apart. Octeon owners: running the test in the write-up and reporting the
result is genuinely useful, whichever way it goes.⬇ browse /poc/ ·
every file has a .sha256 · each tarball carries a NOTES.md ·
built from the v3.0.5 source ·
every trap, in detail:
Proof-of-concept architectures