Anova Precision Cooker (EOL) integration (server, dashboard and sensors) [WARNING advanced]

Hey Community,

I’m not sure if this will interest you, but I’ve been working on a way to keep my first-gen Anova Precision Cooker (which is reaching EOL) connected via WiFi using Home Automation.

After some deep dives into online resources, I stumbled upon anova4all—a great concept that, if I could get working, would be a solid foundation in combination with Home Assistant (HA).

:rotating_light: WARNING: This is advanced and should only be attempted if you’re comfortable with Docker, Raspberry Pi, OpenAPI, configuration editing, HA Core, and system tinkering. This setup is tailored to my environment, so proceed at your own risk! Be aware I use HA Core and this approach is when youre configuring everything yourself (and comfortable with that) and the risks⚠️

The Journey :mag:

  • Initially, I tried running the existing anova4all solution in Docker, but I couldn’t get it working despite hours spent debugging the Go code (which I had never used before!).
  • Then, I discovered Almog Baku’s first version of anova4all in Python (GitHub).
  • After reviewing various other projects, I decided to build my own version based on Almogs research using Python, designed to run on a Raspberry Pi 4 (utilizing its Bluetooth hardware).

:rocket: The Result? A fully working integration! Server, HA integration, and a custom Lovelace panel.



What You Need to Get Started

:white_check_mark: Anova Precision Cooker (WiFi + BLE) (Tested with the first-gen model)
:white_check_mark: Raspberry Pi 4 (Running Raspbian 64-bit, untested on other devices)
:white_check_mark: A Server (Can be the RPi itself, or you can run it in Docker on another machine)

If you want to onboard your Anova to a local WiFi environment, follow this repo:
:arrow_right: [Anova Server GitHub]

For Home Assistant integration & Lovelace setup:
:arrow_right: [Lovelace Panel GitHub]


Advanced: Running BLE in Docker :gear:

Since I run my home lab on a dedicated server, I wanted everything in Docker. However, getting BLE passthrough in Docker was not possible to test because I don’t have it om my amd64. If you want to try it yourself on a RPI or with a dongle, here are some the steps that could do the trick (please write back if it works):

1. Check If Your Host Detects the BLE Device

bluetoothctl list
hciconfig

If your BLE adapter appears, note the interface (e.g., hci0).

2. Run Docker with Proper Permissions

docker run -it --rm \
    --privileged \
    --net=host \
    --device=/dev/bus/usb \
    --device=/dev/ttyUSB0 \
    --device=/dev/ttyS0 \
    --volume /var/run/dbus:/var/run/dbus \
    --cap-add=NET_ADMIN \
    --cap-add=SYS_ADMIN \
    my_ble_container

3. Install BLE Tools Inside the Container

For Debian/Ubuntu-based containers:

apt update && apt install -y bluetooth bluez bluez-tools

For Alpine Linux:

apk add --no-cache bluez

4. Verify BLE Functionality

hciconfig
hciconfig hci0 up
hcitool lescan

5. Troubleshooting :wrench:

  • If hciconfig doesn’t detect hci0, restart Bluetooth:
    service bluetooth restart
    
  • Ensure dbus is running:
    systemctl start dbus
    
  • Check logs for errors:
    journalctl -u bluetooth --no-pager | tail -n 20
    

Would love to hear if anyone else is trying something similar! Drop a comment or check out the GitHub repos.

Cheers,

It’s been a while but after some thinkering I changed the access to the BLE stack, you can now install a seperate instance on for example a RPI. This can be accessed by the server to configure your Anova.

After a redeployment I found a lot of issues so updated that to. Hope this helps everyone who wants to work with this.

Have fun.

Hi Roy!

I’m trying this out now. I have an Anova Precision Cooker 1.0 which is Bluetooth only and currently is connected via an ESP32 Esphome BLE proxy. Hopefully in the coming weeks I will remember to get the 2.0 which is Bluetooth and Wifi from my parents’ house (I forgot to grab it when I was visiting them yesterday, so I’m being thorough and going through your Bluetooth solution right now).

Ok, so I’ve got a nice old and slow Pi Zero W (first gen) that I’ve flashed with DietPi, given a static IP on my network, and have performed the commands from your GitHub:

(first had to) sudo apt install git

git clone https://github.com/RoyOltmans/anova_server.git
cd anova_server

and then from Manual RPi Host Setup (for BLE, native Python)

sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install -y --no-install-recommends \
    software-properties-common gpg-agent python3.11 python3.11-venv python3.11-distutils python3-pip
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
sudo update-alternatives --config python3
python3.11 -m pip install --upgrade pip
pip install pyproject.toml bleak uvicorn fastapi pydantic-settings

The issue is with the last 2 commands, where I get the error: externally-managed-environment

root@DietPi-PiZeroW:~/anova_server# sudo update-alternatives --config python3
There is 1 choice for the alternative python3 (providing /usr/bin/python3).

  Selection    Path                 Priority   Status
------------------------------------------------------------
* 0            /usr/bin/python3.11   1         auto mode
  1            /usr/bin/python3.11   1         manual mode

Press <enter> to keep the current choice[*], or type selection number:
root@DietPi-PiZeroW:~/anova_server# python3.11 -m pip install --upgrade pip
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.
   
    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.
   
    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.
   
    See /usr/share/doc/python3.11/README.venv for more information.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

I probably did the worst thing I could do, which was just to add --break-system-packages to the end of each line as a way to move it forward.

It’s also worth noting that I’m running as root, which I’ve also gotten some warning about when it comes to pip.

More errors result after this that ultimately end with the last command ending as ERROR: Failed building wheel for pydantic-core and that’s as far as I got now.

Any chance you can provide alternate instructions or advice on using a python virtual environment, which seems to be the way all development is encouraged to go these days, or point out if any of my other actions (root, hardware) are an issue?

Thanks!

1 Like

Hi Raykholo!

Great to see you’re trying out the Anova Server — and props for digging into it on a first-gen Pi Zero W, no less!

To be totally honest, my original design focus was around the Anova Precision Cooker with Wi-Fi and BLE, not the BLE-only. The BLE functionality in the server is primarily for initial discovery, configuration, and switching the cooker to local API mode, where it can operate offline without the Anova cloud.

That said, the BLE command/control interface is largely undocumented, and I didn’t implement full command support for the Anova (it could come from the fork, so there could be traces and be working). So you’ll likely hit limitations there — but if you get it working or reverse-engineer those commands, a fork or PR would be amazing!

Now about that externally-managed-environment error (PEP 668)

You hit this because Debian prevents pip from modifying system Python installations, which is why --break-system-packages exists — but yes, that’s the nuclear option and best avoided unless you enjoy reimaging SD cards regularly :wink:

Since you’re running DietPi as root, and on very modest hardware, here’s what I recommend instead:

RECOMMENDED INSTALL (Virtual Environment, Safe Way)

  1. Ensure the right Python is cleanly installed

apt-get update && apt-get upgrade -y

apt-get install -y --no-install-recommends python3.11 python3.11-venv python3.11-distutils python3-pip

  1. Create a virtual environment

cd ~/anova_server

python3.11 -m venv .venv

source .venv/bin/activate

  1. Install dependencies inside the venv

pip install --upgrade pip

pip install -r requirements.txt

Or install manually:

pip install bleak uvicorn fastapi pydantic-settings

This way you avoid system-wide pip installs entirely, and won’t trigger any PEP 668 warnings. Plus, this is the Pythonic way™ for apps nowadays.

About performance on the Pi Zero W

The BLE proxy itself is lightweight and will probably work okay on the Pi Zero W. But running the full FastAPI server alongside it — especially with BLE scanning — may lead to timeouts, dropped packets, or slow web responses. It’s a single-core 32-bit SoC, after all. I developed this on a RPI 4b with a lot more horsepower than the Pi Zero W.

If you want stable performance:

  • Offload the FastAPI server to another host (a Pi 4, VM, or Docker on your NAS).
  • Keep the Pi Zero W as a BLE scanner only using ESPHome BLE proxy or a minimalist script.

Alternative: Docker FTW

If you want to avoid Python VEs and dependency hell altogether, go Docker:

docker build -t anova-server .

docker run --restart unless-stopped -p 8080:8080 --privileged --net=host anova-server

Note: You’ll need --privileged or --device access to /dev/hci0 if using BLE in Docker.

You can even run both the BLE proxy and server on the same host, saving you from passthrough setups.

docker run -p 8000:8000 -p 8080:8080 -e BLE_PROXY_URL=http://[your ble server]:5000 anova-server

Hope this helps

1 Like

Thanks so much for this detailed writeup and assistance! I won’t be able to get to this for a few days, but you’ve clarified a few important things that I didn’t realize before, namely:

I thought the entire point of the BLE stack in the project was to talk to the BLE only Anova, or I guess the full protocol over BLE for either that or the WiFI + BLE model. I didn’t realize that it was just to “pair” with the WiFi + BLE model. Separately, I was wondering how the user / server was going to get the keys to talk to the Anova WiFi device, so again you’ve clarified that.

Just to be absolutely sure, for the WiFi + BLE model, I should be able to use the Pi to do the BLE pairing magic/ key discovery, and then I put that info into the server and never have to run the Pi again? And that is this part of the GitHub instructions?

BLE Setup & WiFi Migration
Pair Anova over BLE:
Use /api/ble/secret_key and save the key and device name.
Push server config:
/api/ble/config_wifi_server — Add the server IP/host and port.
Install WiFi config:
Add your WiFi SSID and password via the API.

I’m a bit of a perfectionist and “exactly so ist” (designing PCBs all day will do that) so in my mind, for the WiFi + BLE model, I’d love to have the docker server running on the same hardware as HA (HAOS bare metal) but I stopped using Portainer some time ago and don’t see another way to run a docker service on HAOS. I could set up Portainer inside HA again just for this if I needed to.
I could absolutely run the docker service on my NAS, as that’s how I run all my other services, but it just strikes me as a bit wrong to require multiple computers on the network to talk to each other just for this when it should all be contained on one box.
I’ve seen your previous comments/ responses that you’re not likely to package this as an HA add-on, and I completely understand + respect your choice.


I'm happy to pull out a Pi 4 for some temporary dev testing of all this (and to save what's left of my sanity, as the Zero W is being quite slow), but I wouldn't be able to justify having it permanently committed when a plethora of 3D Printers await my attention and usage and Klipperization :) When I have some free time to continue with this, I'm happy to explore what the BLE only Anova does here.

None of this is your problem and I’m just rambling about my philosophical ideals about how it “should be”. Thanks again for the detailed assist, and I’ll let you know what I figure out.

1 Like

Hi Raykholo,

My apologies for the late response holidays and work let me to not be able to answer. It’s a head scratcher I understand, I love to try and wrap this in an add on, I am not able to test it. I don’t have the setup, I run proxmox, docker LCX in a mix on a dedicated set of high end HW. All the other peripheral are mainly edge or some connectivity case in my setup.

If you want we can see if it’s easy to port to a add-on, that’s sounds like a project for a Sunday. I will need your help to build it because I don’t have the setup to test it.

Kind regards,

Hi Roy,

No worries, I know “life happens” to all of us and I am just appreciative of whatever time and replies you are able to give here :slight_smile:

Since my last reply above, I did attempt the basics and ran into some roadblocks. I am happy to help with testing the add-on stuff, but that is not the priority if I cannot get the basics working.

So, I have a Pi 4 running the standard Raspberry Pi OS 64-bit, I am SSH’ed in, and the Wifi+BLE Anova is sitting nearby on the desk ready for a success that has not yet occurred.

I tried both the docker build and the python venv instructions above and ran into what appears to be the same issue which is ModuleNotFoundError: No module named 'app'.

ray@raspberrypi-bplus-anova:~/anova_server $ docker build -t anova-server .
[+] Building 201.3s (16/16) FINISHED                                                                                                                              docker:default 
 => [internal] load build definition from Dockerfile                                                                                                                        0.0s 
 => => transferring dockerfile: 899B                                                                                                                                        0.0s 
 => WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 1)                                                                                              0.0s 
 => [internal] load metadata for docker.io/library/ubuntu:22.04                                                                                                             0.8s 
 => [internal] load .dockerignore                                                                                                                                           0.0s 
 => => transferring context: 2B                                                                                                                                             0.0s 
 => [ 1/11] FROM docker.io/library/ubuntu:22.04@sha256:1ec65b2719518e27d4d25f104d93f9fac60dc437f81452302406825c46fcc9cb                                                    11.3s 
 => => resolve docker.io/library/ubuntu:22.04@sha256:1ec65b2719518e27d4d25f104d93f9fac60dc437f81452302406825c46fcc9cb                                                       0.0s 
 => => sha256:1ec65b2719518e27d4d25f104d93f9fac60dc437f81452302406825c46fcc9cb 6.69kB / 6.69kB                                                                              0.0s 
 => => sha256:e8054b48720c54efdb365e6b4340d042a3e29e1eefaa0070ac1e246187605b71 424B / 424B                                                                                  0.0s 
 => => sha256:61cbdeb57a0f863e1780e060468aa64ae41f9a11252196626dfb7d4c2035caf7 2.31kB / 2.31kB                                                                              0.0s 
 => => sha256:ef6d179edc98e93dc6073cb3ddec6f1a6ed1d68d04cd7836a82abfd397922a05 27.36MB / 27.36MB                                                                            7.7s 
 => => extracting sha256:ef6d179edc98e93dc6073cb3ddec6f1a6ed1d68d04cd7836a82abfd397922a05                                                                                   2.6s 
 => [internal] load build context                                                                                                                                           0.2s 
 => => transferring context: 833.14kB                                                                                                                                       0.1s 
 => [ 2/11] WORKDIR /anova                                                                                                                                                  4.4s 
 => [ 3/11] COPY / /anova                                                                                                                                                   0.2s 
 => [ 4/11] RUN apt-get update &&     apt-get upgrade -y &&     apt-get install -y --no-install-recommends     software-properties-common  gpg-agent                       89.7s 
 => [ 5/11] RUN ln -snf /usr/share/zoneinfo/Europe/Amsterdam /etc/localtime && echo Europe/Amsterdam > /etc/timezone                                                        0.4s 
 => [ 6/11] RUN add-apt-repository ppa:deadsnakes/ppa -y                                                                                                                   10.3s 
 => [ 7/11] RUN apt-get install -y --no-install-recommends  python3.11  python3.11-venv  python3.11-distutils     python3-pip                                              42.8s 
 => [ 8/11] RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1                                                                                0.5s 
 => [ 9/11] RUN update-alternatives --config python3                                                                                                                        0.8s 
 => [10/11] RUN python3.11 -m pip install --upgrade pip                                                                                                                     9.5s 
 => [11/11] RUN pip install pyproject.toml bleak uvicorn fastapi pydantic-settings                                                                                         21.0s 
 => exporting to image                                                                                                                                                      9.3s
 => => exporting layers                                                                                                                                                     9.2s
 => => writing image sha256:3bd733a113bee0035c31a4466404b3c017234927ea4772c7982a2c16c19081eb                                                                                0.0s
 => => naming to docker.io/library/anova-server                                                                                                                             0.0s

 2 warnings found (use docker --debug to expand):
 - FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 1)
 - JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals (line 23)
ray@raspberrypi-bplus-anova:~/anova_server $ docker run --restart unless-stopped -p 8080:8080 --privileged --net=host anova-server
WARNING: Published ports are discarded when using host network mode
INFO:     Will watch for changes in these directories: ['/anova']
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [7] using StatReload
Process SpawnProcess-1:
Traceback (most recent call last):
  File "/usr/lib/python3.11/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/usr/lib/python3.11/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.11/dist-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
    target(sockets=sockets)
  File "/usr/local/lib/python3.11/dist-packages/uvicorn/server.py", line 67, in run
    return asyncio.run(self.serve(sockets=sockets))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/base_events.py", line 654, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/uvicorn/server.py", line 71, in serve
    await self._serve(sockets)
  File "/usr/local/lib/python3.11/dist-packages/uvicorn/server.py", line 78, in _serve
    config.load()
  File "/usr/local/lib/python3.11/dist-packages/uvicorn/config.py", line 436, in load
    self.loaded_app = import_from_string(self.app)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/uvicorn/importer.py", line 22, in import_from_string
    raise exc from None
  File "/usr/local/lib/python3.11/dist-packages/uvicorn/importer.py", line 19, in import_from_string
    module = importlib.import_module(module_str)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1126, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1140, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'app'

Here I attempt the python venv instructions, and reach a failure of ERROR: Could not open requirements file: [Errno 2] No such file or directory: 'requirements.txt'

ray@raspberrypi-bplus-anova:~/anova_server $ sudo apt-get install -y --no-install-recommends python3.11 python3.11-venv python3.11-distutils python3-pip
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'python3-distutils' instead of 'python3.11-distutils'
python3-distutils is already the newest version (3.11.2-3).
python3-distutils set to manually installed.
python3-pip is already the newest version (23.0.1+dfsg-1+rpt1).
The following packages were automatically installed and are no longer required:
  criu libintl-perl libintl-xs-perl libmodule-find-perl libnet1 libproc-processtable-perl libprotobuf-c1 libsort-naturally-perl libterm-readkey-perl needrestart
  python3-protobuf tini
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
  libpython3.11 libpython3.11-dev libpython3.11-minimal libpython3.11-stdlib python3.11-dev python3.11-minimal
Suggested packages:
  python3.11-doc binfmt-support
The following packages will be upgraded:
  libpython3.11 libpython3.11-dev libpython3.11-minimal libpython3.11-stdlib python3.11 python3.11-dev python3.11-minimal python3.11-venv
8 upgraded, 0 newly installed, 0 to remove and 190 not upgraded.
Need to get 11.8 MB of archives.
After this operation, 2,048 B of additional disk space will be used.
Get:1 http://deb.debian.org/debian bookworm/main arm64 python3.11-dev arm64 3.11.2-6+deb12u6 [617 kB]
Get:2 http://deb.debian.org/debian bookworm/main arm64 libpython3.11-dev arm64 3.11.2-6+deb12u6 [4,390 kB]
Get:3 http://deb.debian.org/debian bookworm/main arm64 libpython3.11 arm64 3.11.2-6+deb12u6 [1,841 kB]
Get:4 http://deb.debian.org/debian bookworm/main arm64 python3.11-venv arm64 3.11.2-6+deb12u6 [5,896 B]
Get:5 http://deb.debian.org/debian bookworm/main arm64 python3.11 arm64 3.11.2-6+deb12u6 [573 kB]
Get:6 http://deb.debian.org/debian bookworm/main arm64 libpython3.11-stdlib arm64 3.11.2-6+deb12u6 [1,746 kB]
Get:7 http://deb.debian.org/debian bookworm/main arm64 python3.11-minimal arm64 3.11.2-6+deb12u6 [1,859 kB]
Get:8 http://deb.debian.org/debian bookworm/main arm64 libpython3.11-minimal arm64 3.11.2-6+deb12u6 [810 kB]
Fetched 11.8 MB in 3s (3,882 kB/s)
apt-listchanges: Reading changelogs...
(Reading database ... 130293 files and directories currently installed.)
Preparing to unpack .../0-python3.11-dev_3.11.2-6+deb12u6_arm64.deb ...
Unpacking python3.11-dev (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Preparing to unpack .../1-libpython3.11-dev_3.11.2-6+deb12u6_arm64.deb ...
Unpacking libpython3.11-dev:arm64 (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Preparing to unpack .../2-libpython3.11_3.11.2-6+deb12u6_arm64.deb ...
Unpacking libpython3.11:arm64 (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Preparing to unpack .../3-python3.11-venv_3.11.2-6+deb12u6_arm64.deb ...
Unpacking python3.11-venv (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Preparing to unpack .../4-python3.11_3.11.2-6+deb12u6_arm64.deb ...
Unpacking python3.11 (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Preparing to unpack .../5-libpython3.11-stdlib_3.11.2-6+deb12u6_arm64.deb ...
Unpacking libpython3.11-stdlib:arm64 (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Preparing to unpack .../6-python3.11-minimal_3.11.2-6+deb12u6_arm64.deb ...
Unpacking python3.11-minimal (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Preparing to unpack .../7-libpython3.11-minimal_3.11.2-6+deb12u6_arm64.deb ...
Unpacking libpython3.11-minimal:arm64 (3.11.2-6+deb12u6) over (3.11.2-6+deb12u5) ...
Setting up libpython3.11-minimal:arm64 (3.11.2-6+deb12u6) ...
Setting up python3.11-minimal (3.11.2-6+deb12u6) ...
Setting up libpython3.11-stdlib:arm64 (3.11.2-6+deb12u6) ...
Setting up python3.11 (3.11.2-6+deb12u6) ...
Setting up libpython3.11:arm64 (3.11.2-6+deb12u6) ...
Setting up python3.11-venv (3.11.2-6+deb12u6) ...
Setting up libpython3.11-dev:arm64 (3.11.2-6+deb12u6) ...
Setting up python3.11-dev (3.11.2-6+deb12u6) ...
Processing triggers for systemd (252.36-1~deb12u1) ...
Processing triggers for man-db (2.11.2-2) ...
Processing triggers for mailcap (3.70+nmu1) ...
Processing triggers for desktop-file-utils (0.26-1) ...
Processing triggers for gnome-menus (3.36.0-1.1) ...
Processing triggers for libc-bin (2.36-9+rpt2+deb12u10) ...
Scanning processes...
Scanning processor microcode...
Scanning linux images...

Running kernel seems to be up-to-date.

The processor microcode seems to be up-to-date.

No services need to be restarted.

No containers need to be restarted.

No user sessions are running outdated binaries.

No VM guests are running outdated hypervisor (qemu) binaries on this host.
ray@raspberrypi-bplus-anova:~/anova_server $ cd ..
ray@raspberrypi-bplus-anova:~ $ ls
anova_server  Bookshelf  Desktop  Documents  Downloads  Music  Pictures  Public  Templates  Videos
ray@raspberrypi-bplus-anova:~ $ cd ~/anova_server
ray@raspberrypi-bplus-anova:~/anova_server $ 
ray@raspberrypi-bplus-anova:~/anova_server $ ls
'anova_server [HOST]'  'ble_proxy [RPI]'   Dockerfile   pyproject.toml   README.md   startup.sh   uv.lock
ray@raspberrypi-bplus-anova:~/anova_server $ python3.11 -m venv .venv
source .venv/bin/activate

ray@raspberrypi-bplus-anova:~/anova_server $ 
ray@raspberrypi-bplus-anova:~/anova_server $ source .venv/bin/activate
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $ pip install --upgrade pip
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Requirement already satisfied: pip in ./.venv/lib/python3.11/site-packages (23.0.1)
Collecting pip
  Downloading pip-25.2-py3-none-any.whl (1.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 3.3 MB/s eta 0:00:00
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 23.0.1
    Uninstalling pip-23.0.1:
      Successfully uninstalled pip-23.0.1
Successfully installed pip-25.2
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $ 
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $ pip install -r requirements.txt
ERROR: Could not open requirements file: [Errno 2] No such file or directory: 'requirements.txt'

I manually install the requirements instead as per your next instructions.
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $ pip install bleak uvicorn fastapi pydantic-settings
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting bleak
  Downloading bleak-1.0.1-py3-none-any.whl.metadata (5.0 kB)
Collecting uvicorn
  Downloading uvicorn-0.35.0-py3-none-any.whl.metadata (6.5 kB)
Collecting fastapi
  Downloading fastapi-0.116.1-py3-none-any.whl.metadata (28 kB)
Collecting pydantic-settings
  Downloading pydantic_settings-2.10.1-py3-none-any.whl.metadata (3.4 kB)
Collecting dbus-fast>=1.83.0 (from bleak)
  Downloading dbus_fast-2.44.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (10 kB)
Collecting typing-extensions>=4.7.0 (from bleak)
  Downloading typing_extensions-4.14.1-py3-none-any.whl.metadata (3.0 kB)
Collecting click>=7.0 (from uvicorn)
  Downloading click-8.2.1-py3-none-any.whl.metadata (2.5 kB)
Collecting h11>=0.8 (from uvicorn)
  Downloading h11-0.16.0-py3-none-any.whl.metadata (8.3 kB)
Collecting starlette<0.48.0,>=0.40.0 (from fastapi)
  Downloading starlette-0.47.2-py3-none-any.whl.metadata (6.2 kB)
Collecting pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4 (from fastapi)
  Downloading pydantic-2.11.7-py3-none-any.whl.metadata (67 kB)
Collecting annotated-types>=0.6.0 (from pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4->fastapi)
  Downloading https://www.piwheels.org/simple/annotated-types/annotated_types-0.7.0-py3-none-any.whl (13 kB)
Collecting pydantic-core==2.33.2 (from pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4->fastapi)
  Downloading pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (6.8 kB)
Collecting typing-inspection>=0.4.0 (from pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4->fastapi)
  Downloading typing_inspection-0.4.1-py3-none-any.whl.metadata (2.6 kB)
Collecting anyio<5,>=3.6.2 (from starlette<0.48.0,>=0.40.0->fastapi)
  Downloading anyio-4.10.0-py3-none-any.whl.metadata (4.0 kB)
Collecting idna>=2.8 (from anyio<5,>=3.6.2->starlette<0.48.0,>=0.40.0->fastapi)
  Downloading https://www.piwheels.org/simple/idna/idna-3.10-py3-none-any.whl (70 kB)
Collecting sniffio>=1.1 (from anyio<5,>=3.6.2->starlette<0.48.0,>=0.40.0->fastapi)
  Downloading https://www.piwheels.org/simple/sniffio/sniffio-1.3.1-py3-none-any.whl (10 kB)
Collecting python-dotenv>=0.21.0 (from pydantic-settings)
  Downloading python_dotenv-1.1.1-py3-none-any.whl.metadata (24 kB)
Downloading bleak-1.0.1-py3-none-any.whl (135 kB)
Downloading uvicorn-0.35.0-py3-none-any.whl (66 kB)
Downloading fastapi-0.116.1-py3-none-any.whl (95 kB)
Downloading pydantic-2.11.7-py3-none-any.whl (444 kB)
Downloading pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.9/1.9 MB 3.4 MB/s  0:00:00
Downloading starlette-0.47.2-py3-none-any.whl (72 kB)
Downloading anyio-4.10.0-py3-none-any.whl (107 kB)
Downloading pydantic_settings-2.10.1-py3-none-any.whl (45 kB)
Downloading click-8.2.1-py3-none-any.whl (102 kB)
Downloading dbus_fast-2.44.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (879 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 879.6/879.6 kB 2.7 MB/s  0:00:00
Downloading h11-0.16.0-py3-none-any.whl (37 kB)
Downloading python_dotenv-1.1.1-py3-none-any.whl (20 kB)
Downloading typing_extensions-4.14.1-py3-none-any.whl (43 kB)
Downloading typing_inspection-0.4.1-py3-none-any.whl (14 kB)
Installing collected packages: typing-extensions, sniffio, python-dotenv, idna, h11, dbus-fast, click, annotated-types, uvicorn, typing-inspection, pydantic-core, bleak, anyio, 
starlette, pydantic, pydantic-settings, fastapi
Successfully installed annotated-types-0.7.0 anyio-4.10.0 bleak-1.0.1 click-8.2.1 dbus-fast-2.44.3 fastapi-0.116.1 h11-0.16.0 idna-3.10 pydantic-2.11.7 pydantic-core-2.33.2 pydantic-settings-2.10.1 python-dotenv-1.1.1 sniffio-1.3.1 starlette-0.47.2 typing-extensions-4.14.1 typing-inspection-0.4.1 uvicorn-0.35.0
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $ ls
'anova_server [HOST]'  'ble_proxy [RPI]'   Dockerfile   pyproject.toml   README.md   startup.sh   uv.lock
(.venv) ray@raspberrypi-bplus-anova:~/anova_server $ cd ble_proxy\ \[RPI\]/
(.venv) ray@raspberrypi-bplus-anova:~/anova_server/ble_proxy [RPI] $ ls
ble_server.py
(.venv) ray@raspberrypi-bplus-anova:~/anova_server/ble_proxy [RPI] $ nano ble_server.py
(.venv) ray@raspberrypi-bplus-anova:~/anova_server/ble_proxy [RPI] $ uvicorn app.main:app --reload --app-dir ./anova_server/python --port 8000 --host 0.0.0.0
INFO:     Will watch for changes in these directories: ['/home/ray/anova_server/ble_proxy [RPI]']
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [2854] using StatReload
Process SpawnProcess-1:
Traceback (most recent call last):
  File "/usr/lib/python3.11/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/usr/lib/python3.11/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/home/ray/anova_server/.venv/lib/python3.11/site-packages/uvicorn/_subprocess.py", line 80, in subprocess_started
    target(sockets=sockets)
  File "/home/ray/anova_server/.venv/lib/python3.11/site-packages/uvicorn/server.py", line 67, in run
    return asyncio.run(self.serve(sockets=sockets))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/home/ray/anova_server/.venv/lib/python3.11/site-packages/uvicorn/server.py", line 71, in serve
    await self._serve(sockets)
  File "/home/ray/anova_server/.venv/lib/python3.11/site-packages/uvicorn/server.py", line 78, in _serve
    config.load()
  File "/home/ray/anova_server/.venv/lib/python3.11/site-packages/uvicorn/config.py", line 436, in load
    self.loaded_app = import_from_string(self.app)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ray/anova_server/.venv/lib/python3.11/site-packages/uvicorn/importer.py", line 22, in import_from_string
    raise exc from None
  File "/home/ray/anova_server/.venv/lib/python3.11/site-packages/uvicorn/importer.py", line 19, in import_from_string
    module = importlib.import_module(module_str)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1128, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1142, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'app'

I also tried building the python venv same instructions on my Macbook and got the same result of No module named 'app'

When I go in to debug by “trying other things” I was sometimes confused by whether I should always be issuing these commands from the base folder of /anova_server or if I would ever do something from inside a subdir like /anova_server/ble_proxy. I treat the instructions as literally as I can, they say to issue the commands from /anova_server so that’s what I did. But I still have some confusion about how the 2 parts of the project (the anova_server [HOST] and the ble_proxy [RPI]) work together, and hopefully that will become more clear to me once I can get something up and running.


Also, once we (hopefully) get these issues resolved, I’m not clear on future instructions like this and just want to make sure: Is the intention to use a tool like Postman to interact with the API? I know how to do this. Bruno seems to be a less bad version of it I have been using recently. Or do I just go into the browser with those appended to the base path and the browser will render UI to take inputs?

BLE Setup & WiFi Migration

  1. Pair Anova over BLE:
    Use /api/ble/secret_key and save the key and device name.
  2. Push server config:
    /api/ble/config_wifi_server — Add the server IP/host and port.
  3. Install WiFi config:
    Add your WiFi SSID and password via the API.

Thank you, thank you, thank you for any time and assistance you are able to give. I want to get this working (but not in any hurry).

1 Like

Hi RayKholo,

Thanks for pointing this out!

I dug into the warnings and realized it’s likely related to the Docker version I’ve been working with, they didn’t show up for me earlier (or I simply overlooked them). I rebuilt the whole setup on my desktop and was able to reproduce the issue.

  • First, I fixed the CamelCase issue.
  • Second, I corrected the CMD/markup.
  • Third, I noticed the manual was pointing to the wrong Dockerfile that was missing necessary files. I’ve removed the root Dockerfile and pointed everything to the correct one.

After that, I rebuilt and tested the image. I was able to reproduce the missing module error when building inside the anova_server folder — which is now fixed. I think this happened after I added the BLE feature (which I see more as a “nice-to-have”).

Everything should be in good shape now, you can build it as you would like.

1 Like

in anova_server/anova_server
docker build -t anova-server .
docker run --restart unless-stopped -p 8080:8080 --privileged --net=host anova-server

works now! I have access to the API HTML page at one of the ports, the other one does not load anything. Thank you!

Now for the BLE proxy. My understanding now is that BLE proxy is a separate thing that needs to be built/ run separately from anova_server/anova_server.

However, I’m confused, because earlier you said:

So I’m trying to get both anova-server and the BLE proxy running on the same Pi 4, somehow.

The anova-server running in docker returns error about BLE proxy being unreachable:

anova-server-1 | 2025-08-19 03:21:42,138 - anova_api - ERROR - BLE-proxy not reachable: All connection attempts failed

I try to run the python directly but get errors about missing dependencies like before.

export BLE_ADAPTER=hci0
export BLE_PROXY_PORT=5000 # Optional, defaults to 5000
python3 ble_server.py

I tried reusing your venv instructions for ble proxy but it doesn’t appear to run when I issue the python3 ble_server.py command. No response of any sort, just terminal ready for my next command.

Also I tried your docker setup instructions:

ray@raspberrypi-bplus-anova:~/anova_server/ble_proxy $ docker run -it --rm \

    --net=host >     --privileged \
\
    --device=/dev/bus/usb \
    --device=/dev/>     --net=host \
>     --device=/dev/bus/usb \
    --vo>     --device=/dev/ttyUSB0 \
>     --device=/dev/ttyS0 \
>     --volume /var/run/dbus:/var/run/dbus \
--cap-ad>     --cap-add=NET_ADMIN \
>     --cap-add=SYS_ADMIN \
>     my_ble_container
Unable to find image 'my_ble_container:latest' locally
docker: Error response from daemon: pull access denied for my_ble_container, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

Run 'docker run --help' for more information

Appears my_ble_container does not exist anywhere in the git repo that I have seen and docker run won’t make an empty one from scratch.


Any ideas here for any of these methods? I realize I just threw a lot of random things at you - I tried all the ways I know and hit roadblocks. Thanks!

1 Like

The BLE client should be possible to install separately on the same host. You should point the docker -e BLE config to the host IP/name of the RPI with the correct port. In this setup below it’s port 5000 I think the loopback should work try 127.0.0.1 or if you want to separate use LAN ip or hostname (thats up to you).

I apologize upfront, I leave a lot of stuff out (lazy and time constrains, so my apologies).

I will try and make it a step by step manual setup on the same RPI in your case, try this:

Fetch code:

git clone https://github.com/RoyOltmans/anova_server.git
cd anova_server/anova_server

For the ble client in the same folder as the server do:

sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install -y --no-install-recommends \
    software-properties-common gpg-agent python3.11 python3.11-venv python3.11-distutils python3-pip
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
sudo update-alternatives --config python3
python3.11 -m pip install --upgrade pip
pip install pyproject.toml bleak uvicorn fastapi pydantic-setting
uvicorn app.main:app --reload --app-dir ./anova_server/python --port 5000 --host 0.0.0.0

As described in the git you can make the ble proxy a service

Open a second new shell and try the setup below, for the anova server (start at the folder in bash where you fetched the code) and do:

cd anova_server/anova_server
docker build -t anova-server .
docker run -dit --name anova_server --restart unless-stopped -p 8000:8000 -p 8090:8080 -e BLE_PROXY_URL=127.0.0.1:5000 anova-server

Important if you want to control the ports via docker, remove the host tag in the docker run

Now you should be able to open the openapi environment and onboard the anova

remember start with the ble setup:

Pair Anova over BLE:
Use /api/ble/secret_key and save the key and device name.

Push server config:
/api/ble/config_wifi_server — Add the server IP/host and port (thats the anova server).

Install WiFi config:
Add your WiFi SSID and password via the API.

And your all setup to integrate with HA.