Nibe F2040 & SMO 20 with full home app created

Hey everyone,

I wanted to share a project I’ve been building over the last few months to completely take over my Nibe heat pump. I was tired of locked-down ecosystems, basic on/off thermostat logic, and paying subscriptions just to see my own data.

As a software developer who takes an “AI-first” approach to my homelab, I decided to build a complete Building Management System (BMS) from scratch. I’ve just finished sanitizing the codebase and pushing it public as OpenNibe Thermal BMS.

Here is what the system actually does:

:thermometer: The ML Thermal Engine (Heating & Cooling)

Standard thermostats just look at the current temperature. I needed the system to see the future. I built a custom ML .NET (FastTree Regression) pipeline that runs continuously in the background.

  • Winter (Proactive Heating): The engine learns the specific thermal inertia of different rooms. It evaluates the whole-house state and generates a “Global Curve Offset” for the Nibe, proactively heating the slab before the temperature actually drops.

  • Summer (The T+60 Dew Point Watchdog): Underfloor cooling is great until you hit the dew point and ruin your floors. The engine ingests high-frequency telemetry (temp, humidity, solar azimuth, cloud cover) and predicts the indoor dew point exactly 60 minutes into the future. If that forecast (+ a 2.5°C buffer) exceeds the target flow temp, it immediately drops the relays and halts cooling. Zero condensation.

:robot: “AI-First” Agentic Control

Since I run a local Ryzen 7 Pro server with Vulkan-accelerated Ollama, I didn’t just want a dashboard; I wanted an intelligent house.

  • Conversational Control: Using an LLM, I can ask, “Why did the cooling turn off?” and it will query the MlDecisions database, analyze the T+60 forecast, and explain the exact mathematical reasoning.

  • Agentic API: A secured API specifically designed to be consumed by autonomous agents.

  • Daily Briefings: The local AI pulls the day’s compressor hours, Smart Tariff (ToU) costs, and the family calendar, and pushes a summarized morning briefing to my phone.

:date: Family OS & :camera: Security (TC65)

I wanted everything in one pane of glass, so I built it directly into the Blazor Material UI:

  • Calendar & Meal Planning: A fully integrated scheduling system with recurring events, reminders, and automated shopping list generation from our meal plans.

  • RTSP Camera Integration: I integrated MediaMTX to pull WebRTC streams directly from my TC65 security cameras into the UI with virtually zero latency.

:hammer_and_wrench: Hardware & Tinkering

I’m a tinkerer with micro-electronics, so the software is only half the project:

  • Custom Thermostats: I built my own environmental sensors using ESP32-S3s wired to BME280/AHT20 sensors, housed in custom 3D-printed enclosures I designed.

  • SMO20 Telemetry: I have a dedicated ESP32 plugged directly into the Nibe SMO20 that logs data every 10 seconds into InfluxDB, which is then visualized in Grafana.

  • Relay Control: The Nibe F2040 / SMO20 is controlled via local relay spoofing (and MyUplink as a fallback).

:rocket: The Tech Stack & Open Source

  • Backend: .NET 10.0, Entity Framework Core (SQLite for config, InfluxDB for time-series).

  • Frontend: Blazor Server with MudBlazor UI and ApexCharts.

  • Integrations: Open-Meteo API (weather/solar compensation), MediaMTX.

I’ve just abstracted all the configuration and stripped out my specific network topology, so it is now fully open-source and licensed under GPL-3.0.

If you are running a Nibe system, or if you just want to dig into how to build a predictive ML .NET pipeline for HVAC control, you can check out the repository here: OpenNibe-BMS

I am happy to share more of the MicroPython code for the thermostats/humidity sensors as well, or how my SMO20 is configured physically.

Open to critique, suggestions, or ideas on how to push the predictive modeling even further!

How does this version look? It brings the heating, calendar, and cameras right into the spotlight while keeping the awesome ML cooling stuff intact!

Here are some images of the app:

Thanks for reading

Thanks for posting @Dave_C and welcome!

I’d be interested in the hardware side of things - S3s seem massively overpowered for this, be fun to see if you could move the tree processing over to them :slight_smile:

Also, C#, interesting choice :folded_hands: Would it run on a Pi? I know .NET has Linux support, wasn’t sure about AArch64.

Good morning!

Thankyou for your comments, in keeping with my AI first for the project, I asked AI to provide me with a detailed list of what the app does and my ecosystem surrounding it. In doing this it got the ESP32’s the wrong way around :slight_smile:. The thermostats themselves are basic ESP32’s, I had these in stock from other projects and deemed them suitable for controlling the temperature sensors and controlling the optocouplers within the housings. I dare say smaller ESP variants would have been more suitable or for the task, but I like to avoid spending money as well :joy:.

The ESP32 plugged into the USB of the SMO20 in fact is an ESP32-S3, this is due to the need to have USB OTG, so the C code (included in the GitHub link above) emulated a USB key and receives the file data which it parses and posts straight to the InfluxDB endpoint on the server.

The C# code did indeed run on my PI 3b+, albeit slowly and a much more cut down version of the app. So yes it does compile to AArch64.

I chose C# as I use this extensively with my work, and the same for MudBlazor which gives a nice modern front end. The serviced were originally all python scripts, but I recently migrated them to the c# code as well, to keep everything under one roof.

This also personally helped my gain more knowledge as I have used Google Jules almost exclusively to create the project. With some minor interventions needed to clean a few issues up.

I think for most, the benefit of the code I have shared will actually be the C code for the ESP32-S3, as it prevents the need to fetch data from My uplink periodically (this still gets done in the app anyway, as I’m still evaluating the stability of the USB idea).

I hope this answers your questions, and am happy to answer more any time :slight_smile:

Ps am on my mobile, so apologies for spelling and grammar! :laughing: