Read radon (Bq/m3), temperature, and humidity from the Quarta-Rad RADEX MR107 radon monitor on Linux — no proprietary software needed.
Includes SQLite storage, web dashboard with historical charts, CSV export, and Homey Pro integration.
The RADEX MR107 only comes with Windows software (Radex Data Center). This tool reads the sensor directly via USB serial, making it possible to integrate radon monitoring into Linux-based home automation systems like Homey Pro, Home Assistant, or any MQTT-based setup.
- Real-time readings — radon (Bq/m3), temperature (C), humidity (%), plus device averages/min/max
- SQLite database — persistent local storage of all readings
- Web dashboard — dark-themed dashboard with Chart.js graphs, auto-refresh, color-coded radon levels
- CSV export — download all historical data for analysis in Excel/Google Sheets
- Homey Pro integration — push readings to Homey Logic variables via local API
- MQTT support — pipe JSON output to any MQTT broker
- Cold start detection — identifies when the device is still warming up
- Systemd-ready — run as a background service with automatic restart
- Python 3.6+
pyserial(pip install pyserial)- RADEX MR107 connected via USB
- Linux (tested on Debian 13 and Ubuntu 24.04)
git clone https://github.com/Frimurare/radex-mr107-reader.git
cd radex-mr107-reader
pip install pyserialThe MR107 appears as /dev/ttyACM0. Add your user to the dialout group:
sudo usermod -aG dialout $USER
# Log out and back in for the change to take effectVerify the device is connected:
lsusb | grep ABBA
# Should show: ID abba:a004 QUARTA-RAD MR107python3 mr107_reader.pyOutput:
[2026-04-04 16:35:00] Radon: 218.0 Bq/m3 | Temp: 21.0C | Humidity: 35.8% | Avg: 195.2 Max: 312.0 Min: 87.0
python3 mr107_reader.py --json{
"radon_bq": 217.9,
"temperature_c": 20.9,
"humidity_pct": 35.8,
"radon_avg": 195.2,
"radon_max": 312.0,
"radon_min": 87.0,
"countdown_s": 3421,
"cold_start": false,
"timestamp": "2026-04-04 16:35:00"
}python3 mr107_reader.py --loop 60 --db radon.dbThis polls the MR107 every 60 seconds and stores each reading in a SQLite database.
| Option | Description | Default |
|---|---|---|
--port |
Serial port | /dev/ttyACM0 |
--loop N |
Poll every N seconds (0 = single read) | 0 |
--json |
Output as JSON | off |
--db PATH |
SQLite database path | none |
--homey IP |
Homey Pro IP address | none |
--homey-token TOKEN |
Homey API bearer token | none |
--homey-device ID |
Homey Virtual Device ID | none |
A built-in web dashboard for viewing radon statistics in your browser.
python3 dashboard.py --db radon.db --port 8088Then open http://YOUR_IP:8088 in a browser.
- Live sensor cards — radon, temperature, humidity, device average
- Color-coded radon levels — green (<100), yellow (100-200), red (>200 Bq/m3)
- Emoji indicators — 😊 safe, 😬 elevated, 🧟 high
- Historical charts — switchable between 6h, 24h, 3d, 7d, 30d
- Temperature & humidity — dual-axis trend chart
- Statistics — total readings, averages, min/max
- CSV export — download all data as a CSV file
- Database info — shows database size and total readings
- Auto-refresh — updates every 60 seconds
- WHO & Swedish guidelines — reference lines at 100 and 200 Bq/m3
| Option | Description | Default |
|---|---|---|
--db PATH |
SQLite database path | radon.db |
--port N |
Web server port | 8088 |
--bind ADDR |
Bind address | 0.0.0.0 |
Create two systemd services for 24/7 monitoring:
sudo tee /etc/systemd/system/radon-reader.service << 'EOF'
[Unit]
Description=RADEX MR107 Radon Reader
After=network.target
[Service]
Type=simple
User=YOUR_USER
ExecStart=/usr/bin/python3 /path/to/mr107_reader.py --loop 60 --db /path/to/data/radon.db
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOFsudo tee /etc/systemd/system/radon-dashboard.service << 'EOF'
[Unit]
Description=RADEX MR107 Radon Dashboard
After=network.target radon-reader.service
[Service]
Type=simple
User=YOUR_USER
ExecStart=/usr/bin/python3 /path/to/dashboard.py --db /path/to/data/radon.db --port 8088
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOFsudo systemctl daemon-reload
sudo systemctl enable radon-reader radon-dashboard
sudo systemctl start radon-reader radon-dashboard
# Check status
sudo systemctl status radon-reader
sudo systemctl status radon-dashboardPush radon readings to your Homey Pro (2023 or later) via the local REST API. No custom Homey app needed — it uses built-in Logic variables that you can use in Flows.
- Go to https://tools.developer.homey.app/
- Log in with your Athom account
- Select your Homey
- Copy the API token (Bearer token)
python3 mr107_reader.py --loop 60 --db radon.db \
--homey 192.168.1.50 \
--homey-token YOUR_API_TOKEN_HEREReplace 192.168.1.50 with your Homey's IP address (find it in the Homey app under Settings > General).
Open the Homey app and go to More > Logic. You should see three variables updating automatically:
| Variable | Description |
|---|---|
radon_bq |
Current radon level in Bq/m3 |
radon_temperature |
Temperature in C |
radon_humidity |
Humidity in % |
Example: Get a push notification when radon exceeds 200 Bq/m3 (Swedish action level):
- Open Homey app > Flows > Create new Flow
- When... > Logic > "A numeric variable changed" > select
radon_bq - And... > Logic > "Number is greater than" >
200 - Then... > Notifications > "Send push notification" >
"Warning: Radon is above 200 Bq/m3!"
Other useful Flow ideas:
- Alert when radon > 100 Bq/m3 (WHO guideline)
- Alert when radon > 300 Bq/m3 (critical level)
- Daily summary at 08:00 with current radon level
- Turn on ventilation (via smart plug) when radon is high
Add the Homey flags to your systemd service:
ExecStart=/usr/bin/python3 /path/to/mr107_reader.py --loop 60 --db /path/to/data/radon.db --homey 192.168.1.50 --homey-token YOUR_API_TOKEN_HERETo get proper sensor tiles in Homey (with icons, graphs in Insights, and dashboard support), use a Virtual Device. This gives you a real "Radon Monitor" device in Homey with radon, temperature, and humidity readings.
How it works: Homey's Virtual Devices have measure_* capabilities that are read-only — they can't be set directly via the REST API. The reader works around this by creating triggerable Flows that use the "Set a virtual sensor value" action card, then updates the value and triggers the flow automatically each polling cycle.
Open the Homey app > Apps > Search "Virtual Devices" (by Arjan Kranenburg) > Install.
- Homey app > Devices > + (add device)
- Select Virtual Devices > Device
- Name it (e.g., "Radon Monitor MR107")
- Add these capabilities:
measure_radon(Radon) — if availablemeasure_temperature(Temperature)measure_moisture(Humidity/Moisture)
- Place it in the correct zone (e.g., "Basement")
- Save
You need the device ID to pass to the reader. Find it via the API:
curl -s "http://YOUR_HOMEY_IP/api/manager/devices/device/" \
-H "Authorization: Bearer YOUR_TOKEN" | \
python3 -c "import sys,json; data=json.load(sys.stdin);
[print(f'{did}: {d[\"name\"]}') for did,d in data.items() if 'virtual' in d.get('driverUri','')]"python3 mr107_reader.py --loop 60 --db radon.db \
--homey 192.168.1.50 \
--homey-token YOUR_TOKEN \
--homey-device YOUR_DEVICE_ID_HEREThe reader will:
- Update Logic variables (for use in Flows/conditions)
- Auto-create triggerable Flows named "MR107 Set measure_radon" etc.
- Update each flow's value and trigger it every polling cycle
- The virtual device receives the new values and stores them in Homey Insights
- Open Homey app > Dashboards
- Add your Radon Monitor device as a widget
- You now have live radon/temp/humidity tiles in your Homey dashboard
Go to Homey app > Insights > select your Radon Monitor device. Homey stores historical data and shows graphs for each capability.
ExecStart=/usr/bin/python3 /path/to/mr107_reader.py --loop 60 --db /path/to/data/radon.db --homey 192.168.1.50 --homey-token YOUR_TOKEN --homey-device YOUR_DEVICE_IDPipe JSON output to any MQTT broker:
# Publish to MQTT every 5 minutes
python3 mr107_reader.py --json --loop 300 | while read line; do
mosquitto_pub -h mqtt-broker -t "home/radon/mr107" -m "$line"
doneAdd to your Home Assistant configuration.yaml:
mqtt:
sensor:
- name: "Radon Level"
state_topic: "home/radon/mr107"
value_template: "{{ value_json.radon_bq }}"
unit_of_measurement: "Bq/m3"
- name: "Radon Temperature"
state_topic: "home/radon/mr107"
value_template: "{{ value_json.temperature_c }}"
unit_of_measurement: "C"
- name: "Radon Humidity"
state_topic: "home/radon/mr107"
value_template: "{{ value_json.humidity_pct }}"
unit_of_measurement: "%"The dashboard exposes a simple REST API:
| Endpoint | Description |
|---|---|
GET / |
Dashboard HTML page |
GET /api/latest |
Latest reading as JSON |
GET /api/readings?hours=24 |
Readings for the last N hours |
GET /api/stats |
Overall statistics |
GET /api/dbinfo |
Database size and row count |
GET /api/export.csv |
Download all readings as CSV |
| Level | Guideline |
|---|---|
| < 100 Bq/m3 | WHO recommended maximum |
| < 200 Bq/m3 | Swedish action level for existing buildings |
| < 200 Bq/m3 | Swedish limit for new construction |
| > 200 Bq/m3 | Action recommended — improve ventilation |
| > 400 Bq/m3 | Urgent action needed |
The USB protocol was reverse-engineered by intercepting the proprietary Radex Data Center software's communication with the device using Frida dynamic instrumentation.
- USB VID:PID:
0xABBA:0xA004 - Interface: CDC ACM (appears as
/dev/ttyACM0) - Baud rate: 9600, 8N1
A single 18-byte command polls all sensor data:
TX (18 bytes):
7B FF 20 00 06 00 83 04 00 00 DA FB 04 08 0C 00 EF F7
|------- header (12) --------| |--- command (6) ---|
RX (70 bytes):
| Offset | Type | Value |
|---|---|---|
| 24-25 | uint16 LE | Cold start flag (0 = warming up) |
| 28-31 | float LE | Current radon (Bq/m3) |
| 32-35 | float LE | Temperature (C) |
| 36-39 | float LE | Humidity (%) |
| 52-55 | float LE | Average radon |
| 56-59 | float LE | Max radon |
| 60-63 | float LE | Min radon |
| 64-65 | uint16 LE | Countdown timer (seconds) |
All float values are IEEE 754 single-precision, little-endian.
The MR107 stores up to 1000 data points internally (one reading per 4-hour cycle = ~166 days of history). The protocol for downloading stored history has not yet been reverse-engineered — contributions welcome!
| Problem | Solution |
|---|---|
Permission denied: /dev/ttyACM0 |
Add user to dialout group: sudo usermod -aG dialout $USER then log out/in |
No such file: /dev/ttyACM0 |
Check USB connection: lsusb | grep ABBA |
Failed to read MR107 |
Device may be in cold start (wait 1-2 minutes after power on) |
Cold start - warming up |
Normal after power on — the MR107 needs time to begin measuring |
| Dashboard shows no data | Start mr107_reader.py with --db first to populate the database |
This tool was created through clean-room reverse engineering of the USB protocol. It is not affiliated with or endorsed by Quarta-Rad. Use at your own risk.
MIT
Protocol reverse-engineered using Frida dynamic instrumentation. Built by Ulf Holmstrom / Manvarg AB, with Claude Code. Additional protocol reference: Maniak003/RADEX_MR107_Monitor