Breeze Core packages

Signed packages for Breeze Core — self-hosted, LAN-first control for Midea air conditioners. v3.2.0

Both projects on this page are outdated. Breeze Core and the Breeze Android app are published at aspic now, and nothing new will appear here.

The Android app here is Breeze 2.2.7 and will not be updated. Get the current version from aspic or GitHub. It is signed with the same key, so it installs over 2.2.7 and keeps your pairing.

The Breeze Core line here has been sunset. v3.2.0 is the final release of the Python implementation, and its repository is archived. Breeze Core is now breeze-core-native, published at aspic — one static executable of about 2.5 MB with no dependencies at all, where this one installs ~59 MB and an interpreter alongside it.

4.x is a drop-in replacement, not a fork: the same REST API, the same config and device files byte for byte, the same AC_* variables, the same service and package name. It installs over this one and nothing is re-paired. Upgrading is one command.

Everything here stays up. These packages remain published and installable, so an existing 3.2.0 install keeps working and keeps updating from this repository. It simply will not get anything new.
How this works: add the repository once with the snippet for your system, then install and update through your normal package manager. Everything is built and signed offline — this server serves static files and holds no keys, so verify against the published keys. Linux amd64/arm64; amd64 for the BSDs.
Documentation lives in the wiki — installing, pairing, the REST API, and how to expose it safely. This page is just the packages.
Some platforms can lag a release behind. Linux amd64/arm64 is built and signed every release; the BSDs, OpenWrt and RISC-V need real hardware, cross-builds or emulation, so check the package version against v3.2.0 above. The source tarball always matches this page.

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
Why 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.
dnf 4 vs dnf 5. The .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.

No mips package — but the opportunity is no longer mipsed. 🥁 There is a MIPS wheelhouse for OpenWrt, big- and little-endian, built against OpenWrt's own SDK. Not an .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.
RISC-V is here. 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-signed pkg 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 — a pkgin 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
No binary package for OpenBSD. There's no repo to add here: OpenBSD gets the source installer, which builds a self-contained virtualenv under /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 — the os-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.

Its own package, not the FreeBSD one above: OPNsense is 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 service

Unlike 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

winget: manifests for 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+
Outdated: the copy here is Breeze 2.2.7, and it will not be updated. The app has moved to aspic; every release is also on GitHub. Both are signed with the same key as this one, so they install over it. The files below stay for anyone who links to them.

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

⬇ the current version, on aspic · outdated here: breeze-latest.apk (2.2.7) · sha256 · all versions — or grab it from GitHub releases.

App and server version independently. The app talks to any server version and feature-detects what it can use; you don't have to upgrade both together. It needs a Breeze Core server on your network — pair once with the server's API key and an admin approval on the LAN.

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 · Arch
sudo systemctl enable --now breeze-core
sudo systemctl status breeze-core
journalctl -u breeze-core -f            # follow the log

OpenRC

Alpine · Artix (openrc) · Gentoo · Devuan
rc-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 · 15
sudo 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 · 11
echo '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.x
doas 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 developers

These 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_64a frozen bundle for Termux's own interpreter path
s390xlinux-musl-s390xmainframes need cooling too
ppc64lelinux-musl-ppc64lesame recipe as s390x
OpenWrt MIPSmips_24kc (BE) · mipsel_24kc (LE)wheelhouses, built against OpenWrt's SDK — the one that matters
Debian mips64ellinux-glibc-mips64elglibc wheelhouse; Loongson 3, Cavium on Debian. Not for routers
Octeon ⚠mips64_octeonplusUNVERIFIED — 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
⚠ Do not mix the two 32-bit MIPS wheelhouses. 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