BLE body composition scale monitor for Raspberry Pi. Reads weight and impedance data from a GoodPharm TY5108 (BLE name "tzc") scale and displays it on a web dashboard.
- Continuous BLE scanning for scale advertisements
- Body composition calculation using standard BIA formulas (body fat %, muscle mass, BMR, etc.)
- SQLite database for persistent storage
- Web dashboard with weight charts and measurement history
- Raspberry Pi (or any Linux system with Bluetooth LE)
- Python 3.11+
- uv package manager
- GoodPharm TY5108 body composition scale (BLE name: "tzc")
git clone https://github.com/YOUR_USERNAME/scalibur.git
cd scaliburuv syncCopy the example config and edit with your details:
cp config.example.py config.pyEdit config.py with your height, age, and gender for accurate body composition calculations.
# Terminal 1: Start the BLE scanner
uv run python scanner.py
# Terminal 2: Start the dashboard
uv run python dashboard.pyThe dashboard will be available at http://localhost:5000
# Set your Pi's hostname and username
PI_USER=pi ./deploy.sh raspberrypi.localBefore the first deployment, edit the service files to replace YOUR_USER with your Pi username:
# In systemd/scalibur-scanner.service and scalibur-dashboard.service
# Replace YOUR_USER with your actual username (e.g., pi)The deploy script will install and enable the systemd services on first run.
Open http://raspberrypi.local:5000 in your browser.
- Stand on the scale and wait for the measurement to complete
- The scanner detects the BLE advertisement and stores the reading
- View your data on the web dashboard
scanner.py (systemd daemon)
│
│ BLE advertisement
│ └─ filter by name="tzc"
│ └─ wait for measurement complete
│ └─ debounce (30s cooldown)
│
▼
measurements.db (SQLite)
│
▼
dashboard.py (Flask on :5000)
│
└─ Latest reading + weight chart + history
- The web dashboard has no authentication and listens on all interfaces by default
- Only expose on trusted networks or add a reverse proxy with authentication
- The database contains personal health data - keep backups secure
Device: GoodPharm TY5108 body composition scale
BLE Name: tzc
Manufacturer ID: varies (ignored; filter by device name only)
Raw packets are stored as {manufacturer_id:04x}:{manufacturer_data.hex()}.
manufacturer_data bytes:
| Bytes | Description |
|---|---|
| 0-1 | Weight (big-endian, divide by 10 for kg) |
| 2-3 | Impedance (big-endian, divide by 10 for ohms; 0 = not measured) |
| 4-5 | User ID (big-endian) |
| 6 | Status: 0x20 = weight complete, 0x21 = complete with impedance |
| 7+ | MAC address (ignored) |
Example: 74c0:03380000000220fe98000c91d8
- Weight: 0x0338 = 824 → 82.4 kg
- Impedance: 0x0000 = 0 → not measured yet
- User ID: 0x0002 = 2
- Status: 0x20 = weight complete (no impedance)
Body composition is calculated using standard BIA (Bioelectrical Impedance Analysis) formulas in decode.py:
- Lean Body Mass (LBM) from height²/impedance
- Body fat % = (weight - LBM) / weight
- BMR using Mifflin-St Jeor equation
- BMI, muscle mass, water %, bone mass estimates
CREATE TABLE measurements (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
weight_kg REAL NOT NULL,
impedance_raw INTEGER,
impedance_ohm REAL,
body_fat_pct REAL,
fat_mass_kg REAL,
lean_mass_kg REAL,
body_water_pct REAL,
muscle_mass_kg REAL,
bone_mass_kg REAL,
bmr_kcal INTEGER,
bmi REAL
);
CREATE TABLE raw_packets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
packet_hex TEXT NOT NULL
);GPL-3.0 - see LICENSE
