7 Commits

Author SHA1 Message Date
4213b6101a Update debian/changelog for v2.0.0 production release 2025-12-28 11:09:16 +01:00
e9b8d56f6d Release v2.0.0 - Production ready
Version bump:
- Update VERSION to 2.0.0 (from 2.0.0-dev)
- Update pyproject.toml to 2.0.0
- Change development status to Production/Stable

Documentation updates:
- Add Debian package installation instructions (system-wide)
- Add sensor discovery and approval workflow documentation
- Update configuration section with approval workflow
- Update service management for system installation
- Update troubleshooting for system installation
- Update MQTT settings documentation
- Add links to download Debian packages from releases

The application is now production-ready with:
- Systemd integration for automatic service management
- Automatic startup and restart on failure
- Configuration via /etc/sensorpajen/
- Runtime state in /var/lib/sensorpajen/
- Interactive sensor approval workflow
- Automatic configuration reload
- Comprehensive logging via journalctl
2025-12-28 10:54:57 +01:00
a55d065c38 Add bytecode cleanup to postinst before building wheel
Remove stale .pyc files and __pycache__ directories before installing
the sensorpajen package to ensure Python rebuilds bytecode fresh.

This should prevent cached bytecode issues from old syntax errors.
2025-12-28 10:42:16 +01:00
eee68e4034 Fix syntax error in approve_sensors.py
Missing except clause that got accidentally removed during previous edit.
2025-12-28 10:34:28 +01:00
c3dc5677b9 Improve approve-sensors feedback for marking sensors
Changes:
- approve_sensor: Add explicit logging that sensor was marked as approved
- ignore_sensor: Add explicit logging that sensor was marked in discovered_sensors.json
- Both now clearly indicate the sensor status is updated, not just added/ignored

This makes it clear to users that approved sensors will no longer appear as
pending in future runs, since their status in discovered_sensors.json is
changed from 'pending' to 'approved' or 'ignored'.
2025-12-28 10:32:08 +01:00
fc0399a454 Fix system installation state directory
The service was failing with 'Read-only file system' when trying to create
discovered_sensors.json in the /etc/sensorpajen config directory.

Changes:
- config.py: Add STATE_DIR for runtime state
  - System mode: /var/lib/sensorpajen (writable at runtime)
  - Dev mode: config/ (same as config directory)
- config.py: Use STATE_DIR for discovered_sensors.json path
- debian/postinst: Create and own /var/lib/sensorpajen
- debian/sensorpajen.service: Add /var/lib/sensorpajen to ReadWritePaths
- debian/postinst: Remove discovered_sensors.json.example copy (created at runtime)

This separates:
- Config: /etc/sensorpajen (static, not updated by service)
- State: /var/lib/sensorpajen (dynamic, updated by service at runtime)
2025-12-28 09:33:26 +01:00
85af215d73 Fix postinst: Install sensorpajen package in venv
The venv had dependencies installed but not the sensorpajen package itself,
causing 'No module named sensorpajen' errors when running.

Changes:
- After installing dependencies from requirements.txt
- Now also runs 'pip install --no-deps .' to install sensorpajen
- Uses --no-deps to avoid re-installing already-installed dependencies
- Installed in /opt/sensorpajen where pyproject.toml exists

Fixes: ModuleNotFoundError: No module named 'sensorpajen'
2025-12-28 09:29:40 +01:00
17 changed files with 214 additions and 101 deletions

View File

@@ -1 +1 @@
2.0.0-dev 2.0.0

16
debian/changelog vendored
View File

@@ -1,9 +1,19 @@
sensorpajen (2.0.0) stable; urgency=medium
* Production release v2.0.0
* Modernized service architecture with systemd
* Automatic sensor discovery and approval workflow
* Fixed state directory for discovered_sensors.json
* Improved documentation with installation guide
* Bytecode cleanup in postinst for clean installs
-- Fredrik <fredrik@wahlberg.se> Sun, 28 Dec 2025 10:56:00 +0100
sensorpajen (2.0.0-dev) unstable; urgency=medium sensorpajen (2.0.0-dev) unstable; urgency=medium
* Initial Debian package release * Initial Debian package release
* Modernized service architecture with systemd * Modernized service architecture with systemd
* Automatic sensor discovery and approval workflow * 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 -- Fredrik <fredrik@wahlberg.se> Sun, 28 Dec 2025 09:00:00 +0100

1
debian/files vendored
View File

@@ -1,2 +1 @@
sensorpajen_2.0.0-dev_all.deb misc optional sensorpajen_2.0.0-dev_all.deb misc optional
sensorpajen_2.0.0-dev_amd64.buildinfo misc optional

21
debian/postinst vendored
View File

@@ -14,8 +14,13 @@ case "$1" in
chown sensorpajen:sensorpajen /etc/sensorpajen chown sensorpajen:sensorpajen /etc/sensorpajen
chmod 750 /etc/sensorpajen chmod 750 /etc/sensorpajen
# Create state directory with proper permissions (writable at runtime)
mkdir -p /var/lib/sensorpajen
chown sensorpajen:sensorpajen /var/lib/sensorpajen
chmod 750 /var/lib/sensorpajen
# Copy example configs to /etc/sensorpajen if they don't exist # 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 for sample in sensorpajen.env.example sensors.json.example; do
source_file="/usr/share/doc/sensorpajen/examples/$sample" source_file="/usr/share/doc/sensorpajen/examples/$sample"
target_file="/etc/sensorpajen/${sample%.example}" target_file="/etc/sensorpajen/${sample%.example}"
@@ -48,7 +53,7 @@ case "$1" in
venv/bin/pip install -r /opt/sensorpajen/requirements.txt venv/bin/pip install -r /opt/sensorpajen/requirements.txt
else else
echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly" echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly"
venv/bin/pip install bluepy paho-mqtt venv/bin/pip install bluepy paho-mqtt pybluez
fi fi
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@@ -56,6 +61,18 @@ case "$1" in
exit 1 exit 1
fi fi
# Install sensorpajen package itself
echo "Installing sensorpajen application..."
cd /opt/sensorpajen
# Clean up any stale bytecode before building wheel
find . -name "*.pyc" -delete
find . -name "__pycache__" -type d -delete
venv/bin/pip install --no-deps . || {
echo "Error: Failed to install sensorpajen package"
exit 1
}
cd /
# Set ownership of application directory BEFORE setting capabilities # Set ownership of application directory BEFORE setting capabilities
chown -R sensorpajen:sensorpajen /opt/sensorpajen chown -R sensorpajen:sensorpajen /opt/sensorpajen

View File

@@ -26,7 +26,7 @@ SyslogIdentifier=sensorpajen
PrivateTmp=true PrivateTmp=true
ProtectSystem=strict ProtectSystem=strict
ProtectHome=true ProtectHome=true
ReadWritePaths=/etc/sensorpajen ReadWritePaths=/etc/sensorpajen /var/lib/sensorpajen
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -2,7 +2,7 @@ Package: sensorpajen
Version: 2.0.0-dev Version: 2.0.0-dev
Architecture: all Architecture: all
Maintainer: Fredrik <fredrik@wahlberg.se> Maintainer: Fredrik <fredrik@wahlberg.se>
Installed-Size: 110 Installed-Size: 112
Depends: python3 (>= 3.9), python3-venv, python3-pip, bluetooth, bluez, libcap2-bin Depends: python3 (>= 3.9), python3-venv, python3-pip, bluetooth, bluez, libcap2-bin
Recommends: mosquitto-clients Recommends: mosquitto-clients
Section: misc Section: misc

View File

@@ -2,18 +2,18 @@
0894789523a53bb372980c0906a7d0b5 opt/sensorpajen/requirements.txt 0894789523a53bb372980c0906a7d0b5 opt/sensorpajen/requirements.txt
940d73f24eb9f971ce27f9355e3072f3 opt/sensorpajen/scripts/approve-sensors.sh 940d73f24eb9f971ce27f9355e3072f3 opt/sensorpajen/scripts/approve-sensors.sh
20eb4f3839b990a530410768897402c0 opt/sensorpajen/src/sensorpajen/__init__.py 20eb4f3839b990a530410768897402c0 opt/sensorpajen/src/sensorpajen/__init__.py
1f452c46e42f8dc3751dba6ca68256e9 opt/sensorpajen/src/sensorpajen/approve_sensors.py 3c6c65213de874065f81b7b3d8948c8b opt/sensorpajen/src/sensorpajen/approve_sensors.py
da40d9df301d523d517d7cf2809d6f11 opt/sensorpajen/src/sensorpajen/config.py f69225e19918cca05351fa2da8fd7618 opt/sensorpajen/src/sensorpajen/config.py
65c63383dde4f0b249b708f854ec75a3 opt/sensorpajen/src/sensorpajen/discovery_manager.py 65c63383dde4f0b249b708f854ec75a3 opt/sensorpajen/src/sensorpajen/discovery_manager.py
592f8a534833c9e403967fcc0ead8eb1 opt/sensorpajen/src/sensorpajen/main.py 7604c2bc0a854d6d43ff0f0646386fc5 opt/sensorpajen/src/sensorpajen/main.py
331bf9b314492acc6ce03896367f3cf6 opt/sensorpajen/src/sensorpajen/mqtt_publisher.py 331bf9b314492acc6ce03896367f3cf6 opt/sensorpajen/src/sensorpajen/mqtt_publisher.py
5f4ea191e35ce092f39ec0a4f663cb38 opt/sensorpajen/src/sensorpajen/sensor_reader.py 5f4ea191e35ce092f39ec0a4f663cb38 opt/sensorpajen/src/sensorpajen/sensor_reader.py
c8dd8fe8fc174a9cd35251fdf80e7b5f opt/sensorpajen/src/sensorpajen/utils.py c8dd8fe8fc174a9cd35251fdf80e7b5f opt/sensorpajen/src/sensorpajen/utils.py
c9c22f9c1d65bfafd89fa45f16b7192b usr/lib/systemd/system/sensorpajen.service b9ad3ea8307d8ed8e938da37ad00f229 usr/lib/systemd/system/sensorpajen.service
4ddb9618c940286f91df901ec818959a usr/share/doc/sensorpajen/INSTALL.md.gz 4ddb9618c940286f91df901ec818959a usr/share/doc/sensorpajen/INSTALL.md.gz
bd2f1371c60af415bc9d0dbc1111184d usr/share/doc/sensorpajen/ROADMAP.md.gz bd2f1371c60af415bc9d0dbc1111184d usr/share/doc/sensorpajen/ROADMAP.md.gz
380e8e6b01b757ceac05bc5805844ae4 usr/share/doc/sensorpajen/changelog.Debian.gz 380e8e6b01b757ceac05bc5805844ae4 usr/share/doc/sensorpajen/changelog.Debian.gz
14152a98d7cd7fe8daf280aacc4cbf3f usr/share/doc/sensorpajen/examples/discovered_sensors.json.example 14152a98d7cd7fe8daf280aacc4cbf3f usr/share/doc/sensorpajen/examples/discovered_sensors.json.example
387cb9ee7f22570312604e2cc07ca7a0 usr/share/doc/sensorpajen/examples/sensorpajen.env.example 74c99b732363f93f0a1c134e1a8c3d35 usr/share/doc/sensorpajen/examples/sensorpajen.env.example
292efbddd951c39cb2c9546d5fac5e05 usr/share/doc/sensorpajen/examples/sensors.json.example 292efbddd951c39cb2c9546d5fac5e05 usr/share/doc/sensorpajen/examples/sensors.json.example
5f647c63bfc3b174611694779fd215e0 usr/share/doc/sensorpajen/readme.md.gz 5f647c63bfc3b174611694779fd215e0 usr/share/doc/sensorpajen/readme.md.gz

View File

@@ -14,8 +14,13 @@ case "$1" in
chown sensorpajen:sensorpajen /etc/sensorpajen chown sensorpajen:sensorpajen /etc/sensorpajen
chmod 750 /etc/sensorpajen chmod 750 /etc/sensorpajen
# Create state directory with proper permissions (writable at runtime)
mkdir -p /var/lib/sensorpajen
chown sensorpajen:sensorpajen /var/lib/sensorpajen
chmod 750 /var/lib/sensorpajen
# Copy example configs to /etc/sensorpajen if they don't exist # 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 for sample in sensorpajen.env.example sensors.json.example; do
source_file="/usr/share/doc/sensorpajen/examples/$sample" source_file="/usr/share/doc/sensorpajen/examples/$sample"
target_file="/etc/sensorpajen/${sample%.example}" target_file="/etc/sensorpajen/${sample%.example}"
@@ -48,7 +53,7 @@ case "$1" in
venv/bin/pip install -r /opt/sensorpajen/requirements.txt venv/bin/pip install -r /opt/sensorpajen/requirements.txt
else else
echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly" echo "Warning: requirements.txt not found, installing bluepy and paho-mqtt directly"
venv/bin/pip install bluepy paho-mqtt venv/bin/pip install bluepy paho-mqtt pybluez
fi fi
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@@ -56,6 +61,18 @@ case "$1" in
exit 1 exit 1
fi fi
# Install sensorpajen package itself
echo "Installing sensorpajen application..."
cd /opt/sensorpajen
# Clean up any stale bytecode before building wheel
find . -name "*.pyc" -delete
find . -name "__pycache__" -type d -delete
venv/bin/pip install --no-deps . || {
echo "Error: Failed to install sensorpajen package"
exit 1
}
cd /
# Set ownership of application directory BEFORE setting capabilities # Set ownership of application directory BEFORE setting capabilities
chown -R sensorpajen:sensorpajen /opt/sensorpajen chown -R sensorpajen:sensorpajen /opt/sensorpajen

View File

@@ -153,8 +153,10 @@ def approve_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
print(f" Name: {name}") print(f" Name: {name}")
print(f" Configuration will be reloaded automatically within 15 minutes") print(f" Configuration will be reloaded automatically within 15 minutes")
# Mark as approved in discovery manager # Mark as approved in discovery manager and save
print(f"\nUpdating discovery status...")
manager.approve(sensor.mac) manager.approve(sensor.mac)
print(f"✅ Marked as approved in discovered_sensors.json")
except Exception as e: except Exception as e:
print(f"\n❌ Error saving to sensors.json: {e}") print(f"\n❌ Error saving to sensors.json: {e}")
@@ -172,7 +174,7 @@ def ignore_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
manager.ignore(sensor.mac, reason if reason else None) manager.ignore(sensor.mac, reason if reason else None)
print(f"\n✅ Sensor ignored") print(f"\n✅ Sensor ignored and marked in discovered_sensors.json")
if reason: if reason:
print(f" Reason: {reason}") print(f" Reason: {reason}")

View File

@@ -15,14 +15,19 @@ logger = logging.getLogger(__name__)
# Determine project root and config directory # Determine project root and config directory
# Check if running from system installation (/opt/sensorpajen) or development # Check if running from system installation (/opt/sensorpajen) or development
if Path('/opt/sensorpajen').exists(): _opt_sensorpajen_exists = Path('/opt/sensorpajen').exists()
_var_lib_exists = Path('/var/lib/sensorpajen').exists()
if _opt_sensorpajen_exists:
# System installation # System installation
PROJECT_ROOT = Path('/opt/sensorpajen') PROJECT_ROOT = Path('/opt/sensorpajen')
CONFIG_DIR = Path('/etc/sensorpajen') CONFIG_DIR = Path('/etc/sensorpajen')
STATE_DIR = Path('/var/lib/sensorpajen')
else: else:
# Development installation (3 levels up from this file: src/sensorpajen/config.py) # Development installation (3 levels up from this file: src/sensorpajen/config.py)
PROJECT_ROOT = Path(__file__).parent.parent.parent PROJECT_ROOT = Path(__file__).parent.parent.parent
CONFIG_DIR = PROJECT_ROOT / "config" CONFIG_DIR = PROJECT_ROOT / "config"
STATE_DIR = CONFIG_DIR
# MQTT Configuration from environment # MQTT Configuration from environment
MQTT_HOST = os.environ.get("MQTT_HOST") MQTT_HOST = os.environ.get("MQTT_HOST")
@@ -63,7 +68,7 @@ NTFY_TOKEN = os.environ.get("NTFY_TOKEN", "")
# Discovery settings # Discovery settings
DISCOVERED_SENSORS_FILE = os.environ.get( DISCOVERED_SENSORS_FILE = os.environ.get(
"DISCOVERED_SENSORS_FILE", "DISCOVERED_SENSORS_FILE",
str(CONFIG_DIR / "discovered_sensors.json") str(STATE_DIR / "discovered_sensors.json")
) )
CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes
@@ -85,11 +90,11 @@ class SensorConfig:
def load(self): def load(self):
"""Load sensor configuration from JSON file.""" """Load sensor configuration from JSON file."""
if not self.config_file.exists(): if not self.config_file.exists():
raise FileNotFoundError( logger.warning(
f"Sensor configuration file not found: {self.config_file}\n" f"Sensor configuration file not found: {self.config_file}\n"
f"Please copy config/sensors.json.example to config/sensors.json " f"Starting with no sensors - use discovery to add sensors"
f"and configure your sensors."
) )
return
try: try:
with open(self.config_file, 'r') as f: with open(self.config_file, 'r') as f:
@@ -138,6 +143,7 @@ def validate_config():
logger.info(f"Installation Type: {install_type}") logger.info(f"Installation Type: {install_type}")
logger.info(f"Project Root: {PROJECT_ROOT}") logger.info(f"Project Root: {PROJECT_ROOT}")
logger.info(f"Config Directory: {CONFIG_DIR}") logger.info(f"Config Directory: {CONFIG_DIR}")
logger.info(f"State Directory: {STATE_DIR}")
logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}") logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}")
logger.info(f"MQTT User: {MQTT_USER}") logger.info(f"MQTT User: {MQTT_USER}")
logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}") logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}")

View File

@@ -125,9 +125,9 @@ class Sensorpajen:
self.sensor_config = config.SensorConfig() self.sensor_config = config.SensorConfig()
if len(self.sensor_config.sensors) == 0: if len(self.sensor_config.sensors) == 0:
self.logger.error("No sensors configured!") self.logger.warning("No sensors configured")
self.logger.error("Please configure sensors in config/sensors.json") self.logger.warning("Starting in discovery-only mode")
sys.exit(1) self.logger.warning("Use 'sensorpajen approve-sensors' to add sensors")
# Initialize discovery manager # Initialize discovery manager
self.logger.info("Initializing discovery manager...") self.logger.info("Initializing discovery manager...")

View File

@@ -26,7 +26,7 @@ SyslogIdentifier=sensorpajen
PrivateTmp=true PrivateTmp=true
ProtectSystem=strict ProtectSystem=strict
ProtectHome=true ProtectHome=true
ReadWritePaths=/etc/sensorpajen ReadWritePaths=/etc/sensorpajen /var/lib/sensorpajen
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -5,9 +5,16 @@ MQTT_USER=hasse
MQTT_PASSWORD=casablanca MQTT_PASSWORD=casablanca
MQTT_CLIENT_ID=mibridge MQTT_CLIENT_ID=mibridge
# Sensor Configuration (relative to project root) # Sensor Configuration
SENSOR_CONFIG_FILE=config/sensors.json # For system installation (/opt/sensorpajen): Use absolute paths
DISCOVERED_SENSORS_FILE=config/discovered_sensors.json # SENSOR_CONFIG_FILE=/etc/sensorpajen/sensors.json
# DISCOVERED_SENSORS_FILE=/etc/sensorpajen/discovered_sensors.json
#
# For development installation: Use relative paths (from project root)
# SENSOR_CONFIG_FILE=config/sensors.json
# DISCOVERED_SENSORS_FILE=config/discovered_sensors.json
#
# If not set, defaults will be used based on installation type
# Application Settings # Application Settings
WATCHDOG_TIMEOUT=5 WATCHDOG_TIMEOUT=5

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "sensorpajen" name = "sensorpajen"
version = "2.0.0-dev" version = "2.0.0"
description = "Bluetooth temperature sensor monitor for Xiaomi Mijia LYWSD03MMC" description = "Bluetooth temperature sensor monitor for Xiaomi Mijia LYWSD03MMC"
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.9"
@@ -14,7 +14,7 @@ authors = [
] ]
keywords = ["bluetooth", "temperature", "sensor", "mqtt", "raspberry-pi"] keywords = ["bluetooth", "temperature", "sensor", "mqtt", "raspberry-pi"]
classifiers = [ classifiers = [
"Development Status :: 4 - Beta", "Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",

159
readme.md
View File

@@ -22,48 +22,72 @@ Raspberry Pi service that monitors Xiaomi Mijia LYWSD03MMC Bluetooth temperature
## Installation ## Installation
See [INSTALL.md](INSTALL.md) for complete installation instructions. ### System Installation (Debian Package - Recommended for Raspberry Pi)
### Quick Start The easiest way to install on Raspberry Pi OS is using the pre-built Debian package:
```bash ```bash
# Clone repository # Download the latest release
git clone <repo-url> ~/sensorpajen wget https://gitea.wahlberg.se/api/v1/repos/fredrik/sensorpajen/releases/download/v2.0.0/sensorpajen_2.0.0_all.deb
cd ~/sensorpajen
# Create and activate virtual environment # Install
python3 -m venv .venv sudo dpkg -i sensorpajen_2.0.0_all.deb
source .venv/bin/activate
# Install dependencies
pip install -e .
# Configure # Configure
cp config/sensorpajen.env.example config/sensorpajen.env sudo nano /etc/sensorpajen/sensorpajen.env # Edit MQTT settings
cp config/sensors.json.example config/sensors.json sudo systemctl restart sensorpajen
nano config/sensorpajen.env # Edit MQTT settings
nano config/sensors.json # Edit sensor MAC addresses
# Set Bluetooth capabilities # View logs
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(readlink -f .venv/bin/python3) sudo journalctl -u sensorpajen -f
# Install systemd service
mkdir -p ~/.config/systemd/user/
cp systemd/sensorpajen.service ~/.config/systemd/user/
systemctl --user daemon-reload
sudo loginctl enable-linger $USER
# Start service
systemctl --user enable sensorpajen
systemctl --user start sensorpajen
# Check status
systemctl --user status sensorpajen
journalctl --user -u sensorpajen -f
``` ```
The system package installs:
- Application in `/opt/sensorpajen/`
- Configuration in `/etc/sensorpajen/`
- Runtime state in `/var/lib/sensorpajen/`
- Systemd service (runs automatically)
### Development Installation
See [INSTALL.md](INSTALL.md) for complete development setup.
## Configuration ## Configuration
### Quick Setup
After installation, configure your MQTT broker and sensors:
```bash
# Edit MQTT settings
sudo nano /etc/sensorpajen/sensorpajen.env
# Restart service to apply changes
sudo systemctl restart sensorpajen
```
### Approving Sensors (Discovery Workflow)
The service automatically discovers nearby Bluetooth sensors and stores them in a pending list. You approve which ones to monitor:
```bash
# Start sensor discovery (if not already running)
sudo systemctl start sensorpajen
# Let it scan for a minute or two to discover sensors
sleep 120
# View discovered sensors and approve them
sudo sensorpajen approve-sensors
```
The approval CLI will:
1. Show newly discovered sensors with their current readings
2. Ask you to approve, ignore, or skip each sensor
3. Save approved sensors to `/etc/sensorpajen/sensors.json`
4. Mark their status in `/var/lib/sensorpajen/discovered_sensors.json`
When you approve a sensor, it's added to your configuration and the service automatically starts monitoring it.
### MQTT Settings ### MQTT Settings
Edit `config/sensorpajen.env`: Edit `config/sensorpajen.env`:
@@ -71,7 +95,7 @@ Edit `config/sensorpajen.env`:
```bash ```bash
MQTT_HOST=192.168.1.10 MQTT_HOST=192.168.1.10
MQTT_PORT=1883 MQTT_PORT=1883
MQTT_USERNAME=username MQTT_USER=username
MQTT_PASSWORD=password MQTT_PASSWORD=password
MQTT_CLIENT_ID=sensorpajen MQTT_CLIENT_ID=sensorpajen
MQTT_TOPIC_PREFIX=MiTemperature2 MQTT_TOPIC_PREFIX=MiTemperature2
@@ -79,10 +103,11 @@ MQTT_TOPIC_PREFIX=MiTemperature2
### Sensors ### Sensors
Edit `config/sensors.json`: Sensors are automatically managed via the approval workflow. You can also manually edit `/etc/sensorpajen/sensors.json`:
```json ```json
[ {
"sensors": [
{ {
"mac": "A4:C1:38:12:34:56", "mac": "A4:C1:38:12:34:56",
"name": "Living Room" "name": "Living Room"
@@ -92,29 +117,38 @@ Edit `config/sensors.json`:
"name": "Bedroom" "name": "Bedroom"
} }
] ]
``` }
## Service Management ## Service Management
See [systemd/README.md](systemd/README.md) for detailed service management instructions. ### System Installation (Debian Package)
```bash ```bash
# Start/stop service # Start/stop service
systemctl --user start sensorpajen sudo systemctl start sensorpajen
systemctl --user stop sensorpajen sudo systemctl stop sensorpajen
# Enable/disable autostart # Enable/disable autostart
systemctl --user enable sensorpajen sudo systemctl enable sensorpajen
systemctl --user disable sensorpajen sudo systemctl disable sensorpajen
# View status # View status
systemctl --user status sensorpajen sudo systemctl status sensorpajen
# View logs # View logs (live)
journalctl --user -u sensorpajen -f sudo journalctl -u sensorpajen -f
journalctl --user -u sensorpajen -n 100
# View last 50 log lines
sudo journalctl -u sensorpajen -n 50
# Uninstall
sudo dpkg -r sensorpajen
# Note: Configuration is preserved in /etc/sensorpajen/
# To remove config: sudo rm -rf /etc/sensorpajen/
``` ```
### Development Installation
## Flashing New Thermometers ## Flashing New Thermometers
**Important**: Flash only one thermometer at a time! **Important**: Flash only one thermometer at a time!
@@ -158,27 +192,40 @@ sensorpajen/
## Troubleshooting ## Troubleshooting
See [INSTALL.md](INSTALL.md#troubleshooting) for detailed troubleshooting steps. ### System Installation (Debian Package)
### Quick Checks **Service won't start:**
**Permission errors:**
```bash ```bash
sudo setcap 'cap_net_raw,cap_net_admin+eip' $(readlink -f ~/sensorpajen/.venv/bin/python3) # Check what's wrong
systemctl --user restart sensorpajen sudo journalctl -u sensorpajen -n 50
# Check configuration is valid
sudo cat /etc/sensorpajen/sensorpajen.env
# Manually test the application
sudo /opt/sensorpajen/venv/bin/python -m sensorpajen.main
``` ```
**Service status:** **MQTT connection issues:**
```bash ```bash
systemctl --user status sensorpajen # Verify MQTT settings in the log
journalctl --user -u sensorpajen -f sudo journalctl -u sensorpajen | grep MQTT
```
**MQTT test:** # Test MQTT connection manually
```bash
mosquitto_sub -h <MQTT_HOST> -u <USER> -P <PASSWORD> -t "MiTemperature2/#" -v mosquitto_sub -h <MQTT_HOST> -u <USER> -P <PASSWORD> -t "MiTemperature2/#" -v
``` ```
**Sensor not found:**
```bash
# Run sensor discovery
sudo sensorpajen approve-sensors
# Check discovered sensors
sudo cat /var/lib/sensorpajen/discovered_sensors.json | jq '.'
```
### Development Installation
## Development ## Development
```bash ```bash

View File

@@ -153,8 +153,10 @@ def approve_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
print(f" Name: {name}") print(f" Name: {name}")
print(f" Configuration will be reloaded automatically within 15 minutes") print(f" Configuration will be reloaded automatically within 15 minutes")
# Mark as approved in discovery manager # Mark as approved in discovery manager and save
print(f"\nUpdating discovery status...")
manager.approve(sensor.mac) manager.approve(sensor.mac)
print(f"✅ Marked as approved in discovered_sensors.json")
except Exception as e: except Exception as e:
print(f"\n❌ Error saving to sensors.json: {e}") print(f"\n❌ Error saving to sensors.json: {e}")
@@ -172,7 +174,7 @@ def ignore_sensor(sensor: DiscoveredSensor, manager: DiscoveryManager):
manager.ignore(sensor.mac, reason if reason else None) manager.ignore(sensor.mac, reason if reason else None)
print(f"\n✅ Sensor ignored") print(f"\n✅ Sensor ignored and marked in discovered_sensors.json")
if reason: if reason:
print(f" Reason: {reason}") print(f" Reason: {reason}")

View File

@@ -15,14 +15,19 @@ logger = logging.getLogger(__name__)
# Determine project root and config directory # Determine project root and config directory
# Check if running from system installation (/opt/sensorpajen) or development # Check if running from system installation (/opt/sensorpajen) or development
if Path('/opt/sensorpajen').exists(): _opt_sensorpajen_exists = Path('/opt/sensorpajen').exists()
_var_lib_exists = Path('/var/lib/sensorpajen').exists()
if _opt_sensorpajen_exists:
# System installation # System installation
PROJECT_ROOT = Path('/opt/sensorpajen') PROJECT_ROOT = Path('/opt/sensorpajen')
CONFIG_DIR = Path('/etc/sensorpajen') CONFIG_DIR = Path('/etc/sensorpajen')
STATE_DIR = Path('/var/lib/sensorpajen')
else: else:
# Development installation (3 levels up from this file: src/sensorpajen/config.py) # Development installation (3 levels up from this file: src/sensorpajen/config.py)
PROJECT_ROOT = Path(__file__).parent.parent.parent PROJECT_ROOT = Path(__file__).parent.parent.parent
CONFIG_DIR = PROJECT_ROOT / "config" CONFIG_DIR = PROJECT_ROOT / "config"
STATE_DIR = CONFIG_DIR
# MQTT Configuration from environment # MQTT Configuration from environment
MQTT_HOST = os.environ.get("MQTT_HOST") MQTT_HOST = os.environ.get("MQTT_HOST")
@@ -63,7 +68,7 @@ NTFY_TOKEN = os.environ.get("NTFY_TOKEN", "")
# Discovery settings # Discovery settings
DISCOVERED_SENSORS_FILE = os.environ.get( DISCOVERED_SENSORS_FILE = os.environ.get(
"DISCOVERED_SENSORS_FILE", "DISCOVERED_SENSORS_FILE",
str(CONFIG_DIR / "discovered_sensors.json") str(STATE_DIR / "discovered_sensors.json")
) )
CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes CONFIG_RELOAD_INTERVAL = int(os.environ.get("CONFIG_RELOAD_INTERVAL", "900")) # 15 minutes
@@ -138,6 +143,7 @@ def validate_config():
logger.info(f"Installation Type: {install_type}") logger.info(f"Installation Type: {install_type}")
logger.info(f"Project Root: {PROJECT_ROOT}") logger.info(f"Project Root: {PROJECT_ROOT}")
logger.info(f"Config Directory: {CONFIG_DIR}") logger.info(f"Config Directory: {CONFIG_DIR}")
logger.info(f"State Directory: {STATE_DIR}")
logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}") logger.info(f"MQTT Host: {MQTT_HOST}:{MQTT_PORT}")
logger.info(f"MQTT User: {MQTT_USER}") logger.info(f"MQTT User: {MQTT_USER}")
logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}") logger.info(f"MQTT Client ID: {MQTT_CLIENT_ID}")