Phase 8: Implement Debian package creation (2025-12-27)

- Create debian/ directory structure with all required files:
  - control: Package metadata and dependencies
  - compat: Debhelper compatibility level
  - changelog: Version history
  - rules: Build instructions
  - install: File installation mappings
  - postinst: Post-installation setup (user, venv, setcap)
  - prerm: Pre-removal script (stop service)
  - postrm: Post-removal script (cleanup, preserve config)
  - sensorpajen.service: System-wide systemd unit

- Update config.py to support dual-mode operation:
  - Auto-detects system installation (/opt/sensorpajen)
  - Uses /etc/sensorpajen for config in system mode
  - Falls back to PROJECT_ROOT/config for development

- Update scripts/approve-sensors.sh for system paths:
  - Detects system vs development installation
  - Uses correct venv and config paths

- Create scripts/verify-deb.sh: Automated build and verification

- Create debian/README.md: Comprehensive packaging documentation

Package features:
- System-wide installation to /opt/sensorpajen/
- Configuration in /etc/sensorpajen/ (preserved on upgrade/remove)
- Dedicated sensorpajen system user
- Automatic venv creation with dependencies
- Bluetooth capabilities set automatically
- Service auto-enabled but waits for config before starting
- Dual-mode code supports both system and development installations
This commit is contained in:
2025-12-27 23:51:39 +01:00
parent b467541eb5
commit 427df1f034
15 changed files with 1410 additions and 145 deletions

353
debian/README.md vendored Normal file
View File

@@ -0,0 +1,353 @@
# Debian Package Build Guide
This directory contains the Debian packaging files for **sensorpajen**, a Bluetooth temperature sensor monitor for Raspberry Pi.
## Overview
The Debian package installs sensorpajen as a **system-wide service** with:
- Installation to `/opt/sensorpajen/`
- Configuration in `/etc/sensorpajen/`
- Dedicated `sensorpajen` system user
- Systemd service integration
- Automatic Python virtual environment setup
- Bluetooth capability configuration
## Prerequisites
### Required Packages
```bash
sudo apt install \
debhelper \
dpkg-dev \
python3 \
python3-venv \
python3-pip
```
### Optional (for verification)
```bash
sudo apt install lintian
```
## Quick Start
### Automated Build and Verification
```bash
# From project root
./scripts/verify-deb.sh
```
This script will:
1. Check for required tools
2. Build the package
3. Show package contents
4. Run lintian checks
5. Display installation instructions
### Manual Build
```bash
# From project root
dpkg-buildpackage -us -uc -b
```
The `.deb` file will be created in the parent directory:
```bash
ls -lh ../sensorpajen_*.deb
```
## Build Output
```
../sensorpajen_2.0.0-dev_all.deb # Installable package
../sensorpajen_2.0.0-dev_armhf.build # Build log
../sensorpajen_2.0.0-dev_armhf.buildinfo # Build metadata
../sensorpajen_2.0.0-dev_armhf.changes # Changes file
```
## Package Verification
### Check Package Contents
```bash
dpkg-deb -c ../sensorpajen_*.deb
```
### Check Package Metadata
```bash
dpkg-deb -I ../sensorpajen_*.deb
```
### Run Lintian
```bash
lintian ../sensorpajen_*.deb
```
**Note**: Warnings are acceptable. Focus on fixing errors.
## Installation
### On Raspberry Pi
```bash
# Copy package to Pi
scp ../sensorpajen_*.deb pi@raspberrypi:~/
# SSH to Pi and install
ssh pi@raspberrypi
sudo apt install ./sensorpajen_*.deb
```
### Local Testing (Not Recommended)
Installing on your development machine will modify `/opt` and `/etc`:
```bash
sudo apt install ../sensorpajen_*.deb
```
**Warning**: This will create system directories and a system user on your dev machine.
## Post-Installation Configuration
After installing the package:
```bash
# 1. Edit MQTT credentials
sudo nano /etc/sensorpajen/sensorpajen.env
# 2. Configure sensors
sudo nano /etc/sensorpajen/sensors.json
# 3. Start the service
sudo systemctl start sensorpajen
# 4. Check status
sudo systemctl status sensorpajen
# 5. View logs
sudo journalctl -u sensorpajen -f
```
## Package Structure
### Installed Files
| Source | Destination |
|--------|-------------|
| `src/sensorpajen/*.py` | `/opt/sensorpajen/src/sensorpajen/` |
| `scripts/approve-sensors.sh` | `/opt/sensorpajen/scripts/` |
| `pyproject.toml` | `/opt/sensorpajen/` |
| `README.md`, `INSTALL.md`, `ROADMAP.md` | `/usr/share/doc/sensorpajen/` |
| `config/*.example` | `/usr/share/doc/sensorpajen/examples/` |
| `debian/sensorpajen.service` | `/etc/systemd/system/` |
| *(created in postinst)* | `/opt/sensorpajen/venv/` |
| *(created in postinst)* | `/etc/sensorpajen/` |
### Configuration Files
- **Active Config**: `/etc/sensorpajen/sensorpajen.env` (credentials)
- **Active Config**: `/etc/sensorpajen/sensors.json` (sensor list)
- **Discovery Data**: `/etc/sensorpajen/discovered_sensors.json`
- **Examples**: `/usr/share/doc/sensorpajen/examples/*.example`
## Maintainer Scripts
### postinst (Post-Installation)
Runs after package installation:
1. Creates `sensorpajen` system user (if doesn't exist)
2. Creates `/etc/sensorpajen/` directory
3. Copies example configs to `/etc/sensorpajen/` (if missing)
4. Creates Python virtual environment in `/opt/sensorpajen/venv/`
5. Installs Python dependencies via pip
6. Sets Bluetooth capabilities on Python executable
7. Installs systemd service file
8. Enables service (but doesn't start until configured)
### prerm (Pre-Removal)
Runs before package removal:
1. Stops the sensorpajen service
2. Disables the service (on remove, not upgrade)
### postrm (Post-Removal)
Runs after package removal:
1. Removes systemd service file
2. Reloads systemd daemon
3. **Preserves** configuration in `/etc/sensorpajen/`
4. **Preserves** `sensorpajen` user
**Note**: Configuration and user are intentionally preserved to prevent data loss.
## Upgrade Behavior
When upgrading to a new version:
```bash
sudo apt install ./sensorpajen_2.1.0_all.deb
```
- ✅ Service is stopped during upgrade
- ✅ Old files are replaced
- ✅ Configuration in `/etc/sensorpajen/` is **preserved**
- ✅ Python dependencies are updated
- ✅ Service is restarted after upgrade
- ✅ Example files in `/usr/share/doc/` are updated
## Removal Behavior
### Remove (Keep Config)
```bash
sudo apt remove sensorpajen
```
- Service stopped and disabled
- Application files removed from `/opt/sensorpajen/`
- Configuration **preserved** in `/etc/sensorpajen/`
- User **preserved**
### Purge (Still Keeps Config)
```bash
sudo apt purge sensorpajen
```
- Same as remove
- Configuration still **preserved** (by design, for safety)
- User still **preserved**
### Complete Removal
To completely remove everything:
```bash
sudo apt purge sensorpajen
sudo rm -rf /etc/sensorpajen
sudo userdel sensorpajen
```
## Troubleshooting
### Build Fails: "debhelper: command not found"
```bash
sudo apt install debhelper
```
### Build Fails: "dh_python3: command not found"
```bash
sudo apt install dh-python
```
### Lintian Warnings About Permissions
The postinst script runs as root and sets file permissions. This is expected and safe.
### Package Won't Install: Dependency Issues
```bash
# Fix missing dependencies
sudo apt install -f
```
### Service Won't Start After Install
Check if configuration has been edited:
```bash
sudo journalctl -u sensorpajen -n 50
```
Common issues:
- MQTT_HOST still has example value
- sensors.json is empty
- Bluetooth adapter not available
### Bluetooth Capability Not Set
```bash
# Manually set capability
sudo setcap cap_net_raw,cap_net_admin+eip $(readlink -f /opt/sensorpajen/venv/bin/python3)
# Verify
getcap $(readlink -f /opt/sensorpajen/venv/bin/python3)
```
## Development Workflow
### Making Changes
1. Edit source code in `src/sensorpajen/`
2. Update version in `pyproject.toml`
3. Update `debian/changelog` with new entry
4. Rebuild package: `./scripts/verify-deb.sh`
5. Test on Raspberry Pi
### Version Numbering
- Development: `2.0.0-dev`
- Release: `2.0.0`
- Patch: `2.0.1`
Update in both:
- `pyproject.toml` (line 6: `version = "..."`)
- `debian/changelog` (first line)
### Testing on Pi
```bash
# Build
./scripts/verify-deb.sh
# Copy to Pi
scp ../sensorpajen_*.deb pi@raspberrypi:~/
# Install on Pi
ssh pi@raspberrypi
sudo systemctl stop sensorpajen # If upgrading
sudo apt install ./sensorpajen_*.deb
sudo systemctl status sensorpajen
```
## Package Metadata
**Package Name**: sensorpajen
**Section**: misc
**Priority**: optional
**Architecture**: all (pure Python)
**Maintainer**: Fredrik <fredrik@wahlberg.se>
**Depends**: python3 (>= 3.9), python3-venv, python3-pip, bluetooth, bluez, libcap2-bin
**Recommends**: mosquitto-clients
## Additional Resources
- **TASKS.md**: Detailed implementation notes
- **ROADMAP.md**: Phase 8 section for APT package creation
- **INSTALL.md**: User installation guide
- **systemd/README.md**: Service management guide
## Support
For issues or questions:
1. Check `sudo journalctl -u sensorpajen -n 100`
2. Verify configuration files in `/etc/sensorpajen/`
3. Check Bluetooth adapter: `hciconfig`
4. Test MQTT connection: `mosquitto_pub -h <host> -t test -m "test"`
---
**Last Updated**: December 27, 2025
**Package Version**: 2.0.0-dev

9
debian/changelog vendored Normal file
View File

@@ -0,0 +1,9 @@
sensorpajen (2.0.0-dev) unstable; urgency=medium
* Initial Debian package release
* Modernized service architecture with systemd
* Automatic sensor discovery and approval workflow
* Environment-based configuration (no .ini files)
* System-wide installation with dedicated user
-- Fredrik <fredrik@wahlberg.se> Fri, 27 Dec 2025 00:00:00 +0000

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
13

22
debian/control vendored Normal file
View File

@@ -0,0 +1,22 @@
Source: sensorpajen
Section: misc
Priority: optional
Maintainer: Fredrik <fredrik@wahlberg.se>
Build-Depends: debhelper-compat (= 13)
Standards-Version: 4.5.0
Homepage: https://github.com/yourusername/sensorpajen
Package: sensorpajen
Architecture: all
Depends: python3 (>= 3.9), python3-venv, python3-pip, bluetooth, bluez, libcap2-bin, ${misc:Depends}
Recommends: mosquitto-clients
Description: Raspberry Pi Bluetooth temperature sensor monitor
Monitors Xiaomi Mijia LYWSD03MMC temperature sensors via Bluetooth Low Energy
and publishes readings to MQTT broker. Supports ATC firmware with automatic
sensor discovery and approval workflow.
.
Features:
- Automatic sensor discovery
- MQTT publishing
- Systemd service integration
- User approval workflow for new sensors

7
debian/install vendored Normal file
View File

@@ -0,0 +1,7 @@
src/sensorpajen/*.py opt/sensorpajen/src/sensorpajen/
scripts/approve-sensors.sh opt/sensorpajen/scripts/
pyproject.toml opt/sensorpajen/
README.md usr/share/doc/sensorpajen/
INSTALL.md usr/share/doc/sensorpajen/
ROADMAP.md usr/share/doc/sensorpajen/
config/*.example usr/share/doc/sensorpajen/examples/

126
debian/postinst vendored Executable file
View File

@@ -0,0 +1,126 @@
#!/bin/bash
set -e
case "$1" in
configure)
# Create sensorpajen system user if it doesn't exist
if ! getent passwd sensorpajen > /dev/null; then
useradd --system --no-create-home --shell /usr/sbin/nologin sensorpajen
echo "Created system user: sensorpajen"
fi
# Create config directory with proper permissions
mkdir -p /etc/sensorpajen
chown sensorpajen:sensorpajen /etc/sensorpajen
chmod 750 /etc/sensorpajen
# Copy example configs to /etc/sensorpajen if they don't exist
for sample in sensorpajen.env.example sensors.json.example discovered_sensors.json.example; do
source_file="/usr/share/doc/sensorpajen/examples/$sample"
target_file="/etc/sensorpajen/${sample%.example}"
if [ -f "$source_file" ] && [ ! -f "$target_file" ]; then
cp "$source_file" "$target_file"
chown sensorpajen:sensorpajen "$target_file"
# Set restrictive permissions on env file (contains credentials)
if [ "$sample" = "sensorpajen.env.example" ]; then
chmod 600 "$target_file"
echo "Created $target_file (edit this file with your MQTT credentials)"
else
chmod 640 "$target_file"
echo "Created $target_file"
fi
fi
done
# Create virtual environment in /opt/sensorpajen
cd /opt/sensorpajen
if [ ! -d "venv" ]; then
echo "Creating Python virtual environment..."
python3 -m venv venv
venv/bin/pip install --upgrade pip setuptools wheel
fi
# Install Python dependencies from pyproject.toml
echo "Installing Python dependencies..."
venv/bin/pip install -e . || {
echo "Warning: pip install failed. You may need to install dependencies manually."
}
# Set Bluetooth capabilities on Python executable
PYTHON_PATH=$(readlink -f /opt/sensorpajen/venv/bin/python3)
if command -v setcap >/dev/null 2>&1; then
setcap cap_net_raw,cap_net_admin+eip "$PYTHON_PATH" || {
echo "Warning: setcap failed. You may need to run Bluetooth operations as root."
echo "Try: sudo setcap cap_net_raw,cap_net_admin+eip $PYTHON_PATH"
}
else
echo "Warning: setcap not found (install libcap2-bin package)"
fi
# Set ownership of application directory
chown -R sensorpajen:sensorpajen /opt/sensorpajen
# Install systemd service file
if [ -f /opt/sensorpajen/debian/sensorpajen.service ]; then
cp /opt/sensorpajen/debian/sensorpajen.service /etc/systemd/system/
elif [ -f /usr/share/doc/sensorpajen/sensorpajen.service ]; then
cp /usr/share/doc/sensorpajen/sensorpajen.service /etc/systemd/system/
fi
# Reload systemd
systemctl daemon-reload
# Enable service (but don't start - needs configuration first)
systemctl enable sensorpajen.service || {
echo "Warning: Could not enable sensorpajen service"
}
# Check if configuration is ready
if [ -f /etc/sensorpajen/sensorpajen.env ] && [ -f /etc/sensorpajen/sensors.json ]; then
# Check if env file has been configured (not default values)
if grep -q "MQTT_HOST=192.168.0.114" /etc/sensorpajen/sensorpajen.env; then
echo ""
echo "======================================================================"
echo " Configuration needed!"
echo "======================================================================"
echo " Edit /etc/sensorpajen/sensorpajen.env with your MQTT settings"
echo " Edit /etc/sensorpajen/sensors.json with your sensor list"
echo " Then run: sudo systemctl start sensorpajen"
echo "======================================================================"
echo ""
else
# Configuration appears to be customized, restart service
systemctl restart sensorpajen.service && {
echo "Sensorpajen service started"
echo "View logs: sudo journalctl -u sensorpajen -f"
} || {
echo "Failed to start service. Check: sudo systemctl status sensorpajen"
}
fi
else
echo ""
echo "======================================================================"
echo " Sensorpajen installed successfully!"
echo "======================================================================"
echo " Next steps:"
echo " 1. Edit /etc/sensorpajen/sensorpajen.env"
echo " 2. Edit /etc/sensorpajen/sensors.json"
echo " 3. sudo systemctl start sensorpajen"
echo " 4. sudo journalctl -u sensorpajen -f"
echo "======================================================================"
echo ""
fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

41
debian/postrm vendored Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
set -e
case "$1" in
remove)
# Service removed but config and user preserved
echo "Sensorpajen removed. Configuration preserved in /etc/sensorpajen/"
echo "To remove config: sudo rm -rf /etc/sensorpajen/"
# Remove systemd service file
rm -f /etc/systemd/system/sensorpajen.service
systemctl daemon-reload || true
;;
purge)
# Even on purge, we keep config by default (user can manually delete)
# This is safer as it prevents accidental data loss
echo "Configuration preserved in /etc/sensorpajen/"
echo "To remove config: sudo rm -rf /etc/sensorpajen/"
echo "To remove user: sudo userdel sensorpajen"
# Remove systemd service file
rm -f /etc/systemd/system/sensorpajen.service
systemctl daemon-reload || true
# Note: We intentionally do NOT remove:
# - /etc/sensorpajen (contains user data)
# - sensorpajen user (may own other files/processes)
# User must remove these manually if desired
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

27
debian/prerm vendored Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
set -e
case "$1" in
remove|upgrade|deconfigure)
# Stop service before removal or upgrade
if systemctl is-active --quiet sensorpajen.service 2>/dev/null; then
echo "Stopping sensorpajen service..."
systemctl stop sensorpajen.service || true
fi
# Disable service on removal (not upgrade)
if [ "$1" = "remove" ]; then
systemctl disable sensorpajen.service || true
fi
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

16
debian/rules vendored Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_build:
# No build step needed for pure Python
override_dh_auto_install:
# Installation handled by debian/install file
dh_auto_install
override_dh_auto_clean:
# Clean build artifacts
rm -rf build/ dist/ *.egg-info
rm -rf src/*.egg-info

32
debian/sensorpajen.service vendored Normal file
View File

@@ -0,0 +1,32 @@
[Unit]
Description=Sensorpajen - Bluetooth Temperature Sensor Monitor
Documentation=https://github.com/yourusername/sensorpajen
After=network.target bluetooth.target
Wants=bluetooth.target
[Service]
Type=simple
User=sensorpajen
Group=sensorpajen
WorkingDirectory=/opt/sensorpajen
EnvironmentFile=/etc/sensorpajen/sensorpajen.env
ExecStart=/opt/sensorpajen/venv/bin/python -m sensorpajen.main
Restart=always
RestartSec=10
# Bluetooth capabilities require this to be false
NoNewPrivileges=false
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sensorpajen
# Security hardening (where possible with Bluetooth requirements)
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/etc/sensorpajen
[Install]
WantedBy=multi-user.target