Breeze Core packages
Signed packages for Breeze Core — self-hosted, LAN-first control for Midea air conditioners. v3.2.0
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
metadata_expire=1h
EOF
sudo dnf install breeze-core
metadata_expire=1h. dnf's default is 48 hours: once it has refreshed, it will not look at this repository again for two days, so a release published inside that window stays invisible to dnf upgrade and the only apparent cure is dnf clean all. An hourly check is a conditional request for a 3 KB file that answers 304 Not Modified almost every time. Every other package manager here refetches its index on update, so none of them need an equivalent..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-3.2.0.tar.gz \
https://bolero.salataputarica.hr.eu.org/src/breeze-core-3.2.0.tar.gz
tar xzf breeze-core-3.2.0.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-3.2.0; cd breeze-core-3.2.0 'Breeze.BreezeCore.yaml','Breeze.BreezeCore.installer.yaml','Breeze.BreezeCore.locale.en-US.yaml' | % { iwr "https://bolero.salataputarica.hr.eu.org/winget/3.2.0/$_" -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