- Add optional Odoo circuit-breaker for transient failures
- Unify timeout handling in Odoo and MQTT clients
- Improve transient error classification (timeout/connection/5xx/429)
- Add focused unit tests for recovery and circuit-breaker behavior
- Mark Phase 3.3 tasks as completed in optimization plan
- Include device_status_timeout_s in Odoo bridge payload
- Resolve status monitor timeout robustly with backward-compatible fallbacks
- Update running status monitor timeout on POST /config without restart
- Keep compatibility for legacy/local configs without device_status_timeout_s
Result: shaperorigin uses configured 90s timeout for online/offline monitor, preventing 30s flapping.
Problem: Device Status Monitor was using a hardcoded 30-second global timeout
for marking devices offline, independent of the configurable message_timeout_s.
This caused alternating offline/online events for devices with power=0 that
don't send frequent MQTT messages.
Solution: Use the same timeout value (message_timeout_s) for both:
1. Session Detection (message_timeout_s)
2. Device Status Monitoring (device_status_timeout_s)
Implementation:
- Add device_status_timeout_s field to api/models.py DeviceConfig (default: 120s)
- Update Odoo iot_api.py to include device_status_timeout_s in config response
(synchronized with message_timeout_s from device strategy config)
- Update Bridge service_manager.py to use device_status_timeout_s when
initializing DeviceStatusMonitor (fallback to global config if not provided)
Result:
- Single configurable timeout per device in Odoo
- Both checks (session + device status) use same value
- Backward compatible (defaults to 120s if not provided)
- Solves alternating offline/online events for low-power/idle devices
Validation:
- mypy: 0 errors across 47 files
- API model test: device_status_timeout_s field functional
Phase 3.1: Type Safety
- Add bridge_types.py for shared type aliases (EventDict, PowerWatts, Timestamp, DeviceID)
- Define protocols for callbacks and message parsers
- Strict type annotations on all core modules (session_detector, event_queue, device_manager)
- Fix Optional handling and type guards throughout codebase
- Achieve full mypy compliance: 0 errors across 47 source files
Phase 3.2: Logging Unification
- Migrate from stdlib logging to pure structlog across all runtime modules
- Convert all logs to structured event+fields format (snake_case event names)
- Remove f-string and printf-style logger calls
- Add contextvars support for per-request correlation
- Implement FastAPI middleware to bind request_id, http_method, http_path
- Propagate X-Request-ID header in responses
- Remove stdlib logging imports except setup layer (utils/logging.py)
- Ensure log-level consistency across all modules
Files Modified:
- iot_bridge/bridge_types.py (new) - Central type definitions
- iot_bridge/core/* - Type safety and logging unification
- iot_bridge/clients/* - Structured logging with request context
- iot_bridge/parsers/* - Type-safe parsing with structured logs
- iot_bridge/utils/logging.py - Pure structlog setup with contextvars
- iot_bridge/api/server.py - Added request correlation middleware
- iot_bridge/tests/* - Test fixtures updated for type safety
- iot_bridge/OPTIMIZATION_PLAN.md - Phase 3 status updated
Validation:
- mypy . → 0 errors (47 files)
- All unit tests pass
- Runtime behavior unchanged
- API response headers include X-Request-ID
Implemented Phase 2.4 (Dependency Injection Pattern):
- Added new dependencies module with DI container and runtime context
- RuntimeContainer for injectable factories
- RuntimeContext for resolved runtime objects
- create_service_manager() factory
- build_runtime_context() composition root
- Refactored main.py to use dependency container wiring
- Main orchestration now resolves runtime via DI factories
- Reduced direct constructor coupling in entrypoint
- Added unit tests for DI behavior with mocked dependencies
- Verifies factory injection for service manager creation
- Verifies runtime composition uses injected callables
- Updated optimization plan checkboxes for Phase 2.4
Validation:
- py_compile passed for new/changed files
- tests/unit/test_dependencies.py passed
- regression test test_event_queue::test_enqueue passed
Notes:
- Keeps existing runtime behavior unchanged
- Establishes clear composition root for future testability improvements
- Set overall status to in-progress
- Mark Phase 2 as partially completed
- Add implemented Phase 2 commit references
- Check off completed tasks for 2.1, 2.2 and 2.3
- Update timeline table (Phase 0/1 done, Phase 2 partial)
Note: Remaining open checkboxes now reflect work still pending (tests, DI, env hierarchy docs).
- Recreate MQTT client on reconnect to apply broker/auth/TLS changes reliably
- Restart loop on new MQTT client instance after reconnect
- Track loop lifecycle to avoid stale client state
- Include MQTT section in initial ConfigServer current_config state
- Keep /config response consistent with persisted /data/config-active.yaml after restart
Result:
- Broker switches via Odoo push now connect reliably (including TLS/non-TLS changes)
- Bridge startup + persisted config reload now exposes mqtt data correctly via GET /config
- Event flow MQTT -> Bridge -> Odoo remains stable after container restarts
Alle Exception-Klassen haben jetzt sinnvolle __init__-Methoden:
- ConfigurationError: path Parameter für Config-Dateipfad
- ConfigValidationError: field + value für fehlerhafte Felder
- ConnectionError: service Parameter (mqtt/odoo)
- MQTTConnectionError: broker + port Parameter
- DeviceError: device_id Parameter
- ValidationError: field + value für Validierungsfehler
Vorher: Klassen hatten nur 'pass' (technisch korrekt, aber wenig nützlich)
Nachher: Strukturierte Fehlerkontext-Erfassung mit dedizierten Attributen
Beispiel:
# Alt: raise ConfigurationError('File not found', details={'path': ...})
# Neu: raise ConfigurationError('File not found', path='/etc/config.yaml')
Angepasst:
- config/loader.py: Nutzt neuen path-Parameter statt details-Dict
- Alle bestehenden Aufrufe bleiben kompatibel (backward-compatible)
Test: python3 -c 'from exceptions import *; e = MQTTConnectionError(...)'
- Fix ModuleNotFoundError in core/device_manager.py
- Change 'from config_server import BridgeConfig' to 'from api.models import BridgeConfig'
- This was missed during Phase 0 refactoring
- Create modular package structure (core/, clients/, parsers/, api/, config/, utils/)
- Move core business logic to core/ (session_detector, event_queue, device_manager)
- Move external clients to clients/ (mqtt_client, odoo_client)
- Split config.py into config/schema.py (dataclasses) and config/loader.py (I/O)
- Split config_server.py into api/server.py (FastAPI) and api/models.py (Pydantic)
- Create parsers/base.py with MessageParser protocol for extensible parser architecture
- Move utilities to utils/ (logging, status_monitor)
- Update all imports across project (main.py, tests)
- Add __init__.py to all packages with proper exports
- Update README.md with new project structure
- All git mv commands preserve file history
This reorganization improves:
- Code discoverability (clear package responsibilities)
- Maintainability (separation of concerns)
- Extensibility (protocols for parsers, clean API separation)
- Testing (isolated packages easier to mock/test)
See OPTIMIZATION_PLAN.md for full roadmap (Phase 0-5)
Features:
- Added ows.mqtt.bridge model with health status monitoring
- Bridge list/form views with status indicators (Online/Offline/Unknown)
- Scheduled action for periodic health checks (every 2 minutes)
- Health metrics: devices count, subscriptions, last seen timestamp
- Manual health check button in UI
- Smart button linking brokers to bridge instance
Infrastructure:
- Added ows.mqtt.broker model for MQTT broker configurations
- Bridge-Broker relation via Many2one field
- Dynamic MQTT reconnection without container restart
- Robust startup: Bridge starts even when MQTT fails
- TLS reconfiguration limitation documented (paho-mqtt)
Technical Changes:
- Updated models to use @api.model_create_multi for Odoo 18
- Fixed view definitions: tree → list for Odoo 18 compatibility
- Removed mail.thread dependency (unnecessary chatter)
- Added ir.cron for automated health monitoring
- Security/ACL rules for bridge and broker models
- Menu integration under IoT/MQTT section
Documentation:
- Updated IMPLEMENTATION_PLAN.md with new features
- Updated Odoo module README with bridge/broker models
- iot_bridge/README.md already documents dynamic reconnection
Testing:
- All changes tested via Odoo module upgrade
- Health check endpoint verified: GET /health
- Bridge reconnection tested with broker config changes
BREAKING CHANGE: Renamed models for consistent naming convention
- mqtt.device → ows.mqtt.device (table: ows_mqtt_device)
- mqtt.session → ows.mqtt.session (table: ows_mqtt_session)
- ows.iot.event unchanged (table: ows_iot_event)
Changes:
- Updated all Many2one/One2many relations to use new model names
- Updated all env references in controllers and tests
- Updated security CSV file with new model IDs
- Updated all view records (list/form/kanban/pivot/graph/search)
- Fixed controller reference that was still using old mqtt.session
Documentation:
- Added README.md for user-facing module documentation
- Regenerated API.md from updated docstrings
- Regenerated index.html from README.md
Cleanup:
- Removed debug/test files (check_routes.py, test-*.sh/txt)
- Removed obsolete python_proto_type directory
Note: This requires database migration or fresh setup.
Database was reset and module reinstalled successfully.
E2E test with Shelly Simulator passed.
- Implement build script (build_docs.py) with AST parser to auto-generate HTML docs from docstrings
- Add comprehensive Google-style docstrings to all controllers and models
- Create static/description/index.html for Odoo Apps UI with module overview
- Generate api_reference.html (20.5 KB) from source code, linked from Odoo UI
- Add DOCUMENTATION_STRATEGY.md with comparison of 5 documentation approaches
- Create API.md with complete REST API documentation
Device Status Monitoring:
- Implement device_status_monitor.py with health checks and offline detection
- Add /status endpoint for device health overview
- Automatic offline detection after message_timeout_s
Config Push Architecture:
- Add POST /config endpoint to IoT Bridge for dynamic device management
- Auto-push device config from Odoo on create/write/unlink
- Implement device_manager.py for runtime device updates
E2E Tests:
- All 6 E2E tests passing (Create, Update, Push, Delete, Restart, Status Monitor)
- Test coverage for device lifecycle and config synchronization
Documentation is auto-generated via: ./build_docs.sh
View in Odoo: Settings → Apps → Open Workshop MQTT → API Reference
Completes PUSH-only architecture - removes all PULL-based config fetching
REMOVED:
- MockOdooClient.get_config() method (~15 lines)
- Legacy PULL logic: returned hardcoded device config
- No longer used after PUSH architecture (Phase 3.1/3.2)
- OdooClient.get_config() method (~27 lines)
- Legacy PULL logic: GET /ows/iot/config from Odoo
- No longer used after PUSH architecture (Phase 3.1/3.2)
VERIFIED:
- ✅ No periodic config refresh logic in main.py
- ✅ Bridge loads config-active.yaml at startup only
- ✅ Bridge receives config via POST /config (pushed by Odoo)
- ✅ No GET requests to Odoo for config anymore
KEPT (unchanged):
- odoo_client.send_event() - still used for event pushing
- config_server.py GET /config - returns bridge's current config (debugging)
ARCHITECTURE CLEANUP:
Before: Bridge could PULL config from Odoo (get_config)
After: Bridge ONLY receives PUSH from Odoo (POST /config)
- Cleaner separation of concerns
- No periodic polling overhead
- Bridge fully autonomous with config-active.yaml
MODIFIED FILES:
- iot_bridge/odoo_client.py - ~40 lines removed
- IMPLEMENTATION_PLAN.md - Phase 3.3 marked complete
TESTING:
✅ Bridge still runs without config-active.yaml (waits for push)
✅ Bridge loads config-active.yaml at startup
✅ No errors in bridge logs (no missing get_config calls)
✅ Odoo push still works (Phase 3.2 model hooks)
NEXT STEP:
Phase 3.5 - End-to-End Tests (full PUSH architecture validation)
Major architectural improvements to make Bridge resilient:
1. Bridge Autonomy (CRITICAL FIX):
- Remove sys.exit(1) when Odoo config fails (iot_bridge/main.py)
- Bridge now runs autonomously with local config.yaml
- No longer crashes in endless restart loop when Odoo is down
- Odoo connection check becomes optional warning, not blocker
2. Event Type Compatibility:
- Add 'session_ended' to controller event processing (iot_api.py)
- Bridge sends 'session_ended', controller expected 'session_stopped'
- Now accepts both event types for closing sessions
3. Event Type Support:
- Add 'session_ended' to iot_event model selection (iot_event.py)
- Fixes 500 errors when Bridge sends session_ended events
4. Architecture Documentation:
- Update FEATURE_REQUEST with new PUSH architecture (Odoo -> Bridge)
- Update IMPLEMENTATION_PLAN with Phase 3 refactoring plan
- Document autonomous mode and config-push design
- Remove obsolete documentation files
Tested Scenarios:
- ✅ Bridge starts and runs without Odoo
- ✅ Session detection works autonomously
- ✅ Events queue when Odoo is down
- ✅ Queue automatically processes when Odoo returns
- ✅ Sessions close correctly with session_ended events
This enables the next phase: Odoo pushing config to Bridge via HTTP API.