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:
122
ROADMAP.md
122
ROADMAP.md
@@ -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
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user