Phase 2 Complete: Python Package Structure

Core modules created:
- config.py: Environment-based configuration management
  - Loads MQTT settings from environment variables
  - SensorConfig class for JSON sensor mapping
  - Relative path resolution (PROJECT_ROOT)
  - Configuration validation with fail-fast

- mqtt_publisher.py: MQTT client wrapper
  - MQTTPublisher class with connection management
  - Replaces sendToMQTT.sh shell script
  - Direct Python MQTT publishing
  - Automatic reconnection support
  - Optional battery data publishing

- sensor_reader.py: Bluetooth BLE sensor reader
  - SensorReader class for passive BLE scanning
  - ATC firmware packet parsing
  - Duplicate packet filtering via advertisement counter
  - Watchdog thread for BLE recovery
  - Measurement dataclass for type safety

- utils.py: Bluetooth utilities
  - Ported from bluetooth_utils.py (MIT, Colin GUYON)
  - BLE scanning and advertisement parsing functions
  - Linux HCI socket operations

- main.py: Application entry point
  - Sensorpajen main application class
  - Signal handling (SIGTERM/SIGINT) for graceful shutdown
  - Logging to stdout for journald integration
  - Coordinates all components

Architecture:
- Direct Python integration (no shell scripts)
- Clean separation of concerns
- Type hints and dataclasses
- Comprehensive logging
- Graceful shutdown handling

Updated ROADMAP.md to mark Phase 2 as complete.

Next: Phase 3 - Configuration Migration (mostly done in Phase 1)
This commit is contained in:
2025-12-27 13:17:26 +01:00
parent 426f1d3813
commit c9b68dd8e2
6 changed files with 1122 additions and 89 deletions

View File

@@ -122,98 +122,42 @@ Using relative paths for portability across systems:
---
### Phase 2: Python Package Structure ✓ TODO
### Phase 2: Python Package Structure ✅ DONE (2025-12-27)
**Goal**: Create modern Python package with proper entry point
**Notes**:
- Used src/ layout for better packaging practices
- Direct Python MQTT integration (no shell script callbacks)
- ATC firmware BLE advertisement reading (passive scanning)
- Watchdog thread for BLE connection recovery
- Clean separation of concerns (config, MQTT, sensors, main)
#### Tasks:
1. Create `src/sensorpajen/__init__.py`
- Package initialization
- Version information
2. Create `src/sensorpajen/config.py`
- Environment variable loading
- Configuration validation
- Default values
- Fail-fast on missing required config
```python
import os
import json
from pathlib import Path
# MQTT Configuration from environment
MQTT_HOST = os.environ.get("MQTT_HOST")
MQTT_PORT = int(os.environ.get("MQTT_PORT", "1883"))
MQTT_USER = os.environ.get("MQTT_USER")
MQTT_PASSWORD = os.environ.get("MQTT_PASSWORD")
MQTT_CLIENT_ID = os.environ.get("MQTT_CLIENT_ID", "sensorpajen")
# Validate required config
if not MQTT_HOST:
raise RuntimeError("MQTT_HOST environment variable must be set")
(relative to project root)
PROJECT_ROOT = Path(__file__).parent.parent.parent
SENSOR_CONFIG_FILE = os.environ.get(
"SENSOR_CONFIG_FILE",
str(PROJECT_ROOT / "config
str(Path.home() / ".config/sensorpajen/sensors.json")
)
# Bluetooth settings
WATCHDOG_TIMEOUT = int(os.environ.get("WATCHDOG_TIMEOUT", "5"))
ENABLE_BATTERY = os.environ.get("ENABLE_BATTERY", "true").lower() == "true"
```
3. Create `src/sensorpajen/utils.py`
- Port bluetooth_utils.py functionality
- Clean up and modernize
4. Create `src/sensorpajen/sensor_reader.py`
- Extract sensor reading logic from LYWSD03MMC.py
- Remove callback/shell script dependency
- Direct Python MQTT integration
5. Create `src/sensorpajen/mqtt_publisher.py`
- MQTT client setup and connection
- Publishing logic (replacing sendToMQTT.sh)
- Error handling and reconnection
6. Create `src/sensorpajen/main.py`
- Entry point for the application
- Signal handling (SIGTERM, SIGINT)
- Logging setup (to stdout for journald)
- Main loop
```python
#!/usr/bin/env python3
import logging
import signal
import sys
from . import config
from .sensor_reader import SensorReader
from .mqtt_publisher import MQTTPublisher
def main():
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
stream=sys.stdout
)
logger = logging.getLogger(__name__)
logger.info("Starting sensorpajen service")
# Setup signal handlers
def signal_handler(sig, frame):
logger.info("Received shutdown signal")
sys.exit(0)
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
# Main application logic here
# ...
if __name__ == "__main__":
main()
```
- ✅ Created src/sensorpajen/__init__.py with version info
- ✅ Created src/sensorpajen/config.py
- Environment variable loading with validation
- SensorConfig class for JSON sensor mapping
- Relative path resolution (PROJECT_ROOT)
- Configuration validation and logging
- ✅ Created src/sensorpajen/utils.py
- Ported bluetooth_utils.py (MIT licensed, Colin GUYON)
- BLE scanning and advertisement parsing
- ✅ Created src/sensorpajen/mqtt_publisher.py
- MQTTPublisher class with connection management
- Direct publishing (replaces sendToMQTT.sh)
- Automatic reconnection support
- Battery data publishing (optional)
- ✅ Created src/sensorpajen/sensor_reader.py
- SensorReader class for BLE scanning
- ATC packet parsing
- Duplicate packet filtering
- Watchdog for BLE recovery
- Measurement dataclass
- ✅ Created src/sensorpajen/main.py
- Application entry point
- Signal handling (SIGTERM, SIGINT)
- Graceful shutdown
- Logging to stdout for journald
---