diff --git a/.ha_mcp/logs/mcp_usage.jsonl b/.ha_mcp/logs/mcp_usage.jsonl index 5fa609c..534af74 100644 --- a/.ha_mcp/logs/mcp_usage.jsonl +++ b/.ha_mcp/logs/mcp_usage.jsonl @@ -310,3 +310,4 @@ {"timestamp": "2026-07-29T15:59:46.581090+00:00", "tool_name": "ha_call_service", "parameters": {"domain": "nest_snapshot", "service": "capture", "entity_id": null, "data": {"entity_id": "camera.front_door_front_door", "filename": "/media/nest_snapshot_test_{{ now().strftime('%Y%m%d_%H%M%S') }}.jpg"}, "return_response": false, "wait": true, "verbose": false, "result_fields": null, "result_attribute_keys": null, "ws_command": null}, "execution_time_ms": 2592.5869941711426, "success": false, "error_message": "{\n \"success\": false,\n \"error\": {\n \"code\": \"SERVICE_CALL_FAILED\",\n \"message\": \"API error: 500 - 500 Internal Server Error\\n\\nServer got itself in trouble\",\n \"suggestion\": \"Specify an entity_id for targeted service calls\",\n \"suggestions\": [\n \"Specify an entity_id for targeted service calls\",\n \"Check available services for nest_snapshot domain using ha_get_skill_guide\",\n \"Use ha_search() to find correct entity IDs\"\n ]\n },\n \"domain\": \"nest_snapshot\",\n \"service\": \"capture\",\n \"entity_id\": null\n}", "response_size_bytes": null, "user_context": null} {"timestamp": "2026-07-29T16:00:47.876615+00:00", "tool_name": "ha_restart", "parameters": {"confirm": true}, "execution_time_ms": 174.73721504211426, "success": true, "error_message": null, "response_size_bytes": 221, "user_context": null} {"timestamp": "2026-07-29T16:02:57.008302+00:00", "tool_name": "ha_call_service", "parameters": {"domain": "nest_snapshot", "service": "capture", "entity_id": null, "data": {"entity_id": "camera.front_door_front_door", "filename": "/media/nest_snapshot_test_{{ now().strftime('%Y%m%d_%H%M%S') }}.jpg"}, "return_response": false, "wait": true, "verbose": false, "result_fields": null, "result_attribute_keys": null, "ws_command": null}, "execution_time_ms": 2702.7831077575684, "success": false, "error_message": "{\n \"success\": false,\n \"error\": {\n \"code\": \"SERVICE_CALL_FAILED\",\n \"message\": \"API error: 500 - 500 Internal Server Error\\n\\nServer got itself in trouble\",\n \"suggestion\": \"Specify an entity_id for targeted service calls\",\n \"suggestions\": [\n \"Specify an entity_id for targeted service calls\",\n \"Check available services for nest_snapshot domain using ha_get_skill_guide\",\n \"Use ha_search() to find correct entity IDs\"\n ]\n },\n \"domain\": \"nest_snapshot\",\n \"service\": \"capture\",\n \"entity_id\": null\n}", "response_size_bytes": null, "user_context": null} +{"timestamp": "2026-07-29T16:04:15.347187+00:00", "tool_name": "ha_restart", "parameters": {"confirm": true}, "execution_time_ms": 196.81143760681152, "success": true, "error_message": null, "response_size_bytes": 221, "user_context": null} diff --git a/.session_nest_snapshot_20260729.md b/.session_nest_snapshot_20260729.md index 463439b..1637976 100644 --- a/.session_nest_snapshot_20260729.md +++ b/.session_nest_snapshot_20260729.md @@ -3,29 +3,43 @@ ## Goal Capture still images from Nest cameras (Google Nest SDM API) for AI (Ollama) analysis. -## What Was Built (Previous Session) +## What Was Built Custom integration `nest_snapshot` at `/homeassistant/custom_components/nest_snapshot/`: - `manifest.json`, `services.yaml`, `__init__.py` - Uses WebRTC (`aiortc`) to grab one video frame → saves as JPEG - Test automation `automation.test_nest_snapshot_capture` -## What Was Fixed (This Session) -### Bug: `Nest integration data not available for camera.front_door_front_door` +## Bug Fixes Applied -**Root cause:** The integration was written for an older Nest SDM API. In HA 2026.7: -1. Nest stores runtime data in `entry.runtime_data` (a `NestData` dataclass), **not** in `hass.data["nest"]` -2. Camera entity `unique_id` has a `-camera` suffix, but `device_manager.devices` is keyed by the raw Google device name +### Fix 1: `Nest integration data not available` +- **Root cause:** HA 2026.7 stores Nest runtime data in `config_entry.runtime_data` (NestData dataclass), not in `hass.data["nest"]`. Camera entity `unique_id` has `-camera` suffix, but `device_manager.devices` is keyed by raw Google device name. +- **Changes:** Line 69: `config_entry.runtime_data` instead of `hass.data.get(...)`. Lines 78-81: `-camera` suffix stripping before device_manager lookup. +- **Status:** ✅ Fixed -**Changes in `__init__.py`:** -- Line 69: Replaced `hass.data.get(NEST_DOMAIN, {}).get(config_entry_id)` with `config_entry.runtime_data` -- Lines 78-81: Added `-camera` suffix stripping before device_manager lookup +### Fix 2: `aiortc` module not installed +- **Root cause:** `aiortc` and `Pillow` not in manifest.json requirements. +- **Changes:** Added `"requirements": ["aiortc", "Pillow"]` to manifest.json. +- **Status:** ✅ Fixed -## SDP candidate fix (round 3) -- Added `re` import -- Added `_sanitize_sdp()` — inserts missing `1` component ID before `udp` in ICE candidate lines -- Applied `_sanitize_sdp()` to `stream.answer_sdp` before `setRemoteDescription` -- Still pending: restart + test +### Fix 3: SDP format — "Offer must contain audio, video and application m lines" +- **Root cause:** Only added video transceiver; Google needs audio + video + application in order. +- **Changes:** Added `pc.addTransceiver("audio", ...)`, `pc.addTransceiver("video", ...)`, `pc.createDataChannel(...)`. +- **Status:** ✅ Fixed -## To Resume Testing -1. Trigger automation `automation.test_nest_snapshot_capture` or call `nest_snapshot.capture` on `camera.front_door_front_door` -2. Check `/media/` for output file (>10 KB = success, ~1-2 KB = placeholder) +### Fix 4 (current): `invalid literal for int() with base 10: 'udp'` +- **Root cause:** `aiortc` ICE candidate parser expects `candidate:ID udp ...` but Google Nest API sends `candidate:ID udp ...` (missing component number). +- **Attempt 1:** Sanitize via `_sanitize_sdp()` checking `parts[1].lower() == "udp"` — didn't match actual format. +- **Attempt 2:** Changed to `not parts[1].isdigit()` — still failing. Possibly the line format is `a=candidate:udp ...` where `parts[0].split(':')[1]` is `udp` and `parts[1]` IS a digit. +- **Current state:** Added debug logging of raw SDP from Google. Need restart to see logs. +- **Status:** 🔴 In progress — add logging to see actual SDP format + +## File States +- `__init__.py` — has `_sanitize_sdp()` with `not parts[1].isdigit()` check + debug logs +- `manifest.json` — has `["aiortc", "Pillow"]` requirements +- `services.yaml` — unchanged, defines `capture` service +- `AGENTS.local.md` — restart rule added (must ask each time) + +## Pending Changes (committed but not tested after latest edit) +- `_sanitize_sdp()` now uses `not parts[1].isdigit()` (broader match) +- Debug logging added for raw + sanitized SDP +- Need restart → test → check system log for SDP content