Commit Graph

9 Commits

Author SHA1 Message Date
2fb45a6582 feat(device-manager): LWT-based availability monitoring for direct_session devices
- direct_session devices now use availability_topic (LWT) exclusively
  for online/offline state - timeout monitor no longer interferes
- Added availability_managed set: devices in this set bypass
  update_last_seen() and are ignored by the timeout monitor
- Added heartbeat_topics set: heartbeat messages return early before
  the session parser, eliminating direct_session_missing_fields warnings
- Added mark_online_silent() to DeviceStatusMonitor: updates state
  without emitting a duplicate device_online event
- registry.py: added availability_topic + status_topic params for
  direct_session parser type
- server.py: set last_config_update from file mtime on load_persisted_config
- mqtt_bridge.py: auto push config + reset device states when bridge
  comes back from offline (prevents stale state in Odoo after restart)
2026-03-19 18:58:55 +01:00
ea565775b2 feat: parser_config als Wire-Format durchgehend (Phase 3 komplett)
session_config wird nicht mehr über die API/YAML gesendet.
parser_config ist jetzt das einzige Config-Format zwischen Odoo und Bridge.

Änderungen:
- api/models.py: DeviceConfig.session_config → parser_config: dict
  SessionConfig bleibt als internes Modell für SessionDetector
  DeviceConfig.to_session_config() extrahiert Werte mit Defaults
- config/schema.py: DeviceConfig gleich umgestellt + to_session_config()
- config/loader.py: liest parser_config aus YAML, Fallback für legacy
  session_config (Rückwärtskompatibilität für bestehende config-active.yaml)
- core/device_manager.py: device.session_config → device.to_session_config()
- core/service_manager.py: session_config Referenzen entfernt
- Odoo _build_bridge_config: sendet parser_config direkt (+ heartbeat)
- Odoo iot_api.py: gleich umgestellt
- Tests: alle SessionConfig-Fixtures → parser_config dicts
  63/63 passing
2026-03-11 13:05:54 +01:00
ddd1e05f55 feat(bridge): Dynamic Parser Registry – Phase 1
- parsers/registry.py: PARSER_REGISTRY mit shelly_pm-Eintrag; get_parser(),
  get_schema(), list_parser_types() als Public API
- parsers/__init__.py: Registry-Funktionen exportiert
- core/device_manager.py: globalen ShellyParser entfernt; DeviceManager
  verwaltet jetzt ein eigenes parser-Dict pro Device (per get_parser())
- api/server.py: GET /parsers Endpoint hinzugefügt (gibt get_schema() zurück)
- tests/unit/test_parser_registry.py: 17 neue Tests (Registry-API,
  PARSER_REGISTRY-Integrität, DeviceManager-Integration)
- tests/unit/test_device_manager.py: Test auf neues API angepasst (patch
  statt parser=-Argument)

Tests: 63/63 passed
2026-03-10 17:31:03 +01:00
d6a8890340 docs: complete phase 4.4 public API docstrings 2026-02-19 19:27:32 +01:00
e723315e35 Fix: Unify device_status timeout with message_timeout_s
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
2026-02-19 00:09:22 +01:00
ff4ef2f563 Phase 3: Complete type safety & logging unification (3.1-3.2)
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
2026-02-18 23:54:27 +01:00
527b5645ed refactor(phase2.3): Migrate to Pydantic V2 and modernize config management
Complete Pydantic V2 migration and config modernization:

api/models.py:
- Migrated @validator → @field_validator (Pydantic V2)
- Added @classmethod decorators (required in V2)
- Updated validator signatures: (cls, v, values) → (cls, v, info)
- Consolidated unique validators into one method
- Fixed: 3 PydanticDeprecatedSince20 warnings 

config/schema.py:
- Migrated from @dataclass → Pydantic BaseModel
- Added Field() with validation constraints:
  * Port: ge=1, le=65535
  * Floats: gt=0, ge=0
  * Strings: min_length constraints
- Added descriptive field descriptions
- Better type safety and runtime validation

core/bootstrap.py:
- Replaced SimpleNamespace → MQTTConfig (Pydantic)
- Now returns properly typed Pydantic models
- Better IDE autocomplete and type checking

Benefits:
 No more Pydantic V1 deprecation warnings
 Runtime validation for all config fields
 Better error messages on invalid config
 Type-safe config throughout the application
 Automatic validation (port ranges, positive numbers, etc.)
 Prepared for Pydantic V3 migration

Migration verified:
- Config loading works: load_config('config.example.yaml') ✓
- Bootstrap works with Pydantic models ✓
- Field validation works (tested port ranges, thresholds) ✓
- All existing functionality preserved ✓

Test: python3 -c 'from core.bootstrap import bootstrap; bootstrap()'
 Works perfectly with new Pydantic V2 models

See OPTIMIZATION_PLAN.md Phase 2.3 for details.
2026-02-18 22:53:36 +01:00
e675256e0e chore: Apply code quality tools (black, isort, ruff)
Automated code quality improvements:

Black (Code Formatter):
- Reformatted 30 files to match PEP 8 style
- Consistent line length (100 chars)
- Consistent quote style and spacing

isort (Import Sorter):
- Fixed import order in 30+ files
- Grouped imports: stdlib → third-party → first-party → local
- Consistent multi-line import formatting

Ruff (Linter):
- Auto-fixed 161 issues:
  * Modernized type hints (List → list, Optional[X] → X | None)
  * Removed unused imports (json, os, typing utilities)
  * Updated deprecated typing imports (typing.List → list)
  * Simplified type annotations with | operator (Python 3.10+)
- Remaining 20 issues (acceptable):
  * 9x unused-method-argument (callback signatures, cannot be changed)
  * 2x bare-except (intentional for resilience)
  * 2x unused-variable (will be addressed in Phase 2)
  * Minor simplification opportunities (not critical)

Code Quality Stats:
- 32 files modified
- +1749/-1589 lines (net cleanup)
- Code is now consistent and formatted
- Ready for gradual type safety improvements (Phase 3)

All changes are non-functional - pure formatting and import organization.
2026-02-18 22:20:25 +01:00
a5929ed290 Phase 0: Reorganize directory structure for better code organization
- 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)
2026-02-18 21:48:14 +01:00