diff --git a/README.md b/README.md index 6cfd46f..0740249 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Once connected, your AI coding assistant can browse documentation, interpret fra ## 🛠️ Tools & Resources Reference -### MCP Tools (14) +### MCP Tools (15) | Tool | Description | |---|---| @@ -87,7 +87,8 @@ Once connected, your AI coding assistant can browse documentation, interpret fra | `list_who_catalog` | Summary inventory table of all 20+ OpenWebNet WHO families with archive status. | | `get_ha_guide` | Full markdown guide for configuring and troubleshooting Home Assistant MyHOME platforms. | | `lookup_frame_syntax` | Grammar, regex templates, and parameter formats for OpenWebNet message types. | -| `parse_and_validate_frame` | Deep syntax and semantic validation of any raw OpenWebNet frame string. | +| `parse_and_validate_frame` | Deep syntax and semantic validation of any raw OpenWebNet frame string, enriched with empirical firmware oracle verification. | +| `lookup_firmware_verdict` | Direct lookup of empirical gateway firmware behavior (MH200N, MyHomeServer1) from live QEMU execution (ACK/NACK, bus telegrams, SHA-256 provenance). | | `draft_own_frame` | Construct and validate a syntactically correct OpenWebNet frame string. | | `draft_sound_source_selection` | Build the WHO=16 frame pair that switches a room's audio source, including which amplifiers the change reaches and how far the evidence for it goes. | | `draft_ha_config` | Generate production-ready Home Assistant configuration YAML for MyHOME entities. | @@ -95,14 +96,15 @@ Once connected, your AI coding assistant can browse documentation, interpret fra | `search_knowledge` | Ranked search over the Encyclopedia's Machine KB (atomic claims and retrieval chunks). Every hit keeps its epistemic status, applicability, provenance, cautions and open questions. | | `get_knowledge_record` | Resolve any Machine KB stable ID (`ownkb:claim:…`, `ownkb:chunk:…`, `ownkb:caution:…`, …) with cautions and questions hydrated. | | `get_knowledge_status` | Which Machine KB snapshot backs the answers: versions, artifact-hash verification, counts. | -| `rescan_documentation` | Flush caches and reload all OpenWebNet specifications, documents, and AST models, and the Machine KB. | +| `rescan_documentation` | Flush caches and reload all OpenWebNet specifications, documents, AST models, Machine KB, and Firmware Oracle verdicts. | -### MCP Resources (5) +### MCP Resources (6) | URI | Description | |---|---| | `spec://who-catalog` | Read-only catalog of all OpenWebNet WHO families. | | `spec://protocol-grammar` | Read-only formal OpenWebNet grammar, regex patterns, and session specs. | +| `oracle://index` | Read-only diagnostic summary of the empirical firmware oracle index (gateway platforms, versions, total inputs). | | `docs://toc` | Master Table of Contents for all indexed OpenWebNet & MyHOME documentation. | | `docs://guide/{topic}` | Read-only full text of a specific documentation guide. | | `kb://manifest` | Read-only Machine KB manifest: exact dataset, compatibility versions and artifact hashes. | @@ -111,7 +113,7 @@ Once connected, your AI coding assistant can browse documentation, interpret fra | Prompt / Slash Command | Description | |---|---| -| `/boost [topic]` | Injects authoritative OpenWebNet protocol architecture, WHO subsystem mappings, frame delimiters, and modern Home Assistant `/config/myhome.yaml` standards directly into the AI agent context window. | +| `/boost [topic]` | Injects authoritative OpenWebNet protocol architecture, WHO subsystem mappings, frame delimiters, modern Home Assistant `/config/myhome.yaml` standards, and firmware verdict guidance directly into the AI agent context window. | #### Using `/boost` in MCP Clients In Claude Desktop, Cursor, or Antigravity, trigger the prompt by typing `/boost` or selecting it from the prompt menu: @@ -150,6 +152,26 @@ intact source chunk; DALI / WHO 24 is not in the corpus yet. Ranking is lexical A live test of `kb_fetch` against the real release is opt-in: `OPENWEBNET_LIVE_TESTS=1 pytest -m network`. +--- + +## 🛡️ Empirical Firmware Oracle + +The `lookup_firmware_verdict` tool and `parse_and_validate_frame` integration provide instant, low-latency inspection of real gateway firmware behavior captured by the [own-firmware-oracle](https://github.com/OpenWebNet-HA/own-firmware-oracle) project. + +- **Execution Environment**: Verdicts reflect live OpenWebNet commands executed inside instrumented QEMU emulators running binary firmware images for BTicino MH200N (`010108`) and MyHomeServer1 (`028206`). +- **Ground Truth Grounding**: Each verdict captures: + - Exact client session reply: `ACK (*#*1##)`, `NACK (*#*0##)`, `SILENT`, or `*No client reply*`. + - Dispatched SCS bus telegrams (hex bytes) or confirmation that no bus frames were emitted. + - Verdict classification: `OUT (Bus)` (physically dispatched to the bus) vs `SILENT (Drop)` (consumed/dropped internally). + - Secondary session notifications and status events (`emitted_own`). + - Cryptographic provenance: canonical JSON SHA-256 integrity verification and per-target SHA-256 hashes. +- **Resolution Order**: + 1. `OPENWEBNET_ORACLE_INDEX` environment variable (explicit path to `mcp_index.json`). + 2. Sibling checkout: `../own-firmware-oracle/results/mcp_index.json`. + 3. Bundled index snapshot: `openwebnet_mcp/data/oracle_index.json`. + +--- + ## 📚 Master WHO Family Inventory | WHO | Subsystem | Official Title | Status | HA Platform | diff --git a/src/openwebnet_mcp/_paths.py b/src/openwebnet_mcp/_paths.py index 5a8544b..2915152 100644 --- a/src/openwebnet_mcp/_paths.py +++ b/src/openwebnet_mcp/_paths.py @@ -2,14 +2,18 @@ from __future__ import annotations +import logging import os import re import sys from pathlib import Path +logger = logging.getLogger("openwebnet_mcp.paths") + _APP_NAME = "openwebnet-mcp" + def get_package_root() -> Path: """Return the path to src/openwebnet_mcp.""" return Path(__file__).resolve().parent @@ -168,3 +172,39 @@ def get_ownd_repo_path() -> Path | None: if cand.exists(): return cand return None + + +def resolve_oracle_index() -> tuple[Path | None, str]: + """Resolve path to oracle index and its source tier ('env', 'sibling', 'bundled', or 'none').""" + env_path = os.environ.get("OPENWEBNET_ORACLE_INDEX") + if env_path: + p = Path(env_path) + if p.is_file(): + return p, "env" + logger.warning( + "OPENWEBNET_ORACLE_INDEX is set to '%s' but the file does not exist; falling back to sibling/bundled index.", + env_path, + ) + + sibling_index = get_project_root().parent / "own-firmware-oracle" / "results" / "mcp_index.json" + if sibling_index.is_file(): + return sibling_index, "sibling" + + bundled = get_data_dir() / "oracle_index.json" + if bundled.is_file(): + return bundled, "bundled" + + return None, "none" + + +def get_oracle_index_path() -> Path | None: + """Return path to own-firmware-oracle mcp_index.json if available. + + Resolution order: + 1. OPENWEBNET_ORACLE_INDEX environment variable (explicit file path) + 2. Sibling own-firmware-oracle checkout: /own-firmware-oracle/results/mcp_index.json + 3. Bundled internal data fallback if present: get_data_dir() / "oracle_index.json" + """ + path, _ = resolve_oracle_index() + return path + diff --git a/src/openwebnet_mcp/data/oracle_index.json b/src/openwebnet_mcp/data/oracle_index.json new file mode 100644 index 0000000..d991031 --- /dev/null +++ b/src/openwebnet_mcp/data/oracle_index.json @@ -0,0 +1,2372 @@ +{ + "format_version": "1.0.0", + "gateways": [ + { + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "product": "MH200N", + "suites": [ + "energy-params", + "interface-routing", + "lights-level", + "matrix-refused", + "ownd-pr82-lights", + "thermo-actuator", + "thermo-central-mode", + "thermo-fan-speed", + "thermo-valve", + "thermo-zone-mode", + "what19", + "who25-messages" + ], + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "version": "010108" + }, + { + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "product": "MyHomeServer1", + "suites": [ + "lights-level", + "ownd-pr82-heating", + "ownd-pr82-lights" + ], + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "version": "028206" + } + ], + "generator": "own-firmware-oracle", + "schema_version": "1.0.0", + "total_unique_inputs": 89, + "verdicts": { + "*#1*0*#1*100*0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 16, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*#1*1*#1*20##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#1*31*#1*100*0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 17, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 16, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*#1*31*#1*100*255##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 18, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*#1*31*#1*100*5##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 19, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 17, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*#1*31*#1*101*0##": [ + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 30 31 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 30 31 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 30 31 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 20, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 30 31 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 18, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*#1*31*#1*150*5##": [ + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 33 32 30 35 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 33 32 30 35 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 19, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*#1*31*#1*200*0##": [ + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 36 34 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 44 36 34 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 20, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*#1*31*#1*220*0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 21, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 21, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*#1*74##": [ + { + "bus_frames": [ + "24 30 33 37 34 30 30 31 35 30 30 0d" + ], + "direction": "down", + "emitted_own": [ + "*1*19*74##", + "*#1001*74*11*111110111111111111110111##" + ], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "-", + "source_tsv": "results/MH200N/010108/oracle/full/what19.tsv", + "suite": "what19", + "suite_sha256": "f3c061a836cac17ee6bdf5bccab64d53e5c15998c1bb73a737cb27cedda18be4", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*#18*51*51##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/energy-params.tsv", + "suite": "energy-params", + "suite_sha256": "964322cfdd7a0c940d08853d84d54a43c9e44101ca19e01616553d474d7301a2", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#18*51*511#10#1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/energy-params.tsv", + "suite": "energy-params", + "suite_sha256": "964322cfdd7a0c940d08853d84d54a43c9e44101ca19e01616553d474d7301a2", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#18*51*511#10#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/energy-params.tsv", + "suite": "energy-params", + "suite_sha256": "964322cfdd7a0c940d08853d84d54a43c9e44101ca19e01616553d474d7301a2", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#18*51*52##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/energy-params.tsv", + "suite": "energy-params", + "suite_sha256": "964322cfdd7a0c940d08853d84d54a43c9e44101ca19e01616553d474d7301a2", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#18*51*52#26#9##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/energy-params.tsv", + "suite": "energy-params", + "suite_sha256": "964322cfdd7a0c940d08853d84d54a43c9e44101ca19e01616553d474d7301a2", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#2*1*#1*50##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*#0#2*#11*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*#0*#11*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*#0*20##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-actuator.tsv", + "suite": "thermo-actuator", + "suite_sha256": "7d1b7c4a97ecc16d1df9254e69ab09c3b6134cad6093e3961e0ba962e54b325a", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*#1*#11*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 16, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*#4*#99*#11*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 17, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*#4*1#1*20##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-actuator.tsv", + "suite": "thermo-actuator", + "suite_sha256": "7d1b7c4a97ecc16d1df9254e69ab09c3b6134cad6093e3961e0ba962e54b325a", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*1*#11*0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*1*#11*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 31 30 33 30 32 30 42 30 31 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 18, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*#4*1*#11*2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 21, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*1*#11*3##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 22, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*1*#11*4##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 23, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*1*19##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-valve.tsv", + "suite": "thermo-valve", + "suite_sha256": "d9ee737015eefa43db2093d8a059e278aeaff08e4fcec133a314addc7e5a79bf", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*1*20##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-actuator.tsv", + "suite": "thermo-actuator", + "suite_sha256": "7d1b7c4a97ecc16d1df9254e69ab09c3b6134cad6093e3961e0ba962e54b325a", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*40*19##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-valve.tsv", + "suite": "thermo-valve", + "suite_sha256": "d9ee737015eefa43db2093d8a059e278aeaff08e4fcec133a314addc7e5a79bf", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*41*19##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-valve.tsv", + "suite": "thermo-valve", + "suite_sha256": "d9ee737015eefa43db2093d8a059e278aeaff08e4fcec133a314addc7e5a79bf", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*99*#11*0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 24, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*99*#11*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 25, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 36 33 30 33 30 32 30 42 30 31 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 19, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*#4*99*#11*2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 26, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*#4*99*#11*3##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 27, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-fan-speed.tsv", + "suite": "thermo-fan-speed", + "suite_sha256": "96b9b84ce01bb1500269d9242fa143146fd73640e2bfb42f30282f8b038a8bf5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*0*1*31#4#01##": [ + { + "bus_frames": [ + "24 30 36 45 43 30 31 30 30 30 30 33 31 30 30 31 34 30 31 0d", + "24 30 36 45 43 30 31 30 30 30 30 33 31 30 30 31 34 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "-", + "source_tsv": "results/MH200N/010108/oracle/full/interface-routing.tsv", + "suite": "interface-routing", + "suite_sha256": "a9a61928900628b6d4e03c40cfbda733ce060f3ad21463befb784e8c184a8968", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*0*1*31#4#02##": [ + { + "bus_frames": [ + "24 30 36 45 43 30 32 30 30 30 30 33 31 30 30 31 34 30 31 0d", + "24 30 36 45 43 30 32 30 30 30 30 33 31 30 30 31 34 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "-", + "source_tsv": "results/MH200N/010108/oracle/full/interface-routing.tsv", + "suite": "interface-routing", + "suite_sha256": "a9a61928900628b6d4e03c40cfbda733ce060f3ad21463befb784e8c184a8968", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*1*0#0*31##": [ + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 21, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 21, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*1*0#1*31##": [ + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 22, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 22, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*1*0#255*31##": [ + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 46 46 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 23, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 46 46 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 23, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*1*0#5*31##": [ + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 35 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 24, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 35 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 22, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 35 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 24, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + }, + { + "bus_frames": [ + "24 30 36 44 31 33 31 30 31 34 32 30 44 30 31 30 30 30 35 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 22, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*1*0*31##": [ + { + "bus_frames": [ + "24 30 33 33 31 30 30 31 32 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 25, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 33 33 31 30 30 31 32 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 23, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 33 33 31 30 30 31 32 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 25, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + }, + { + "bus_frames": [ + "24 30 33 33 31 30 30 31 32 30 31 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 23, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-lights.tsv", + "suite": "ownd-pr82-lights", + "suite_sha256": "57c7e14d5ecbfb59443b3a15a20fe8092e368fa7a7070dc669078175dfd80707", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*1*1*31##": [ + { + "bus_frames": [ + "24 30 33 33 31 30 30 31 32 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 26, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 33 33 31 30 30 31 32 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 26, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/lights-level.tsv", + "suite": "lights-level", + "suite_sha256": "f1d47d88ed4afd662fbff17e9dc28967be295cfbea444b5b5eb824c1dc0f093b", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*14*0*31#4#01##": [ + { + "bus_frames": [ + "24 30 36 45 43 30 31 30 30 30 30 33 31 30 30 31 31 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/interface-routing.tsv", + "suite": "interface-routing", + "suite_sha256": "a9a61928900628b6d4e03c40cfbda733ce060f3ad21463befb784e8c184a8968", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*14*0*31#4#02##": [ + { + "bus_frames": [ + "24 30 36 45 43 30 32 30 30 30 30 33 31 30 30 31 31 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "ack", + "source_tsv": "results/MH200N/010108/oracle/full/interface-routing.tsv", + "suite": "interface-routing", + "suite_sha256": "a9a61928900628b6d4e03c40cfbda733ce060f3ad21463befb784e8c184a8968", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*15*01#1*0001##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*15*01#2*0001##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*15*01#3*0001##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 21, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*15*01*0001##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 22, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*2*1*21#4#1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 23, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*25*11#121*10##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/who25-messages.tsv", + "suite": "who25-messages", + "suite_sha256": "57f36863e445aa963e48f4bfcd45297256dab5a2842c6b3c9944e66a845f7bb5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*25*12*131##": [ + { + "bus_frames": [ + "24 30 34 42 31 31 46 39 34 30 30 0d", + "24 32 34 0d", + "24 32 34 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/who25-messages.tsv", + "suite": "who25-messages", + "suite_sha256": "57f36863e445aa963e48f4bfcd45297256dab5a2842c6b3c9944e66a845f7bb5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*25*21#1*21##": [ + { + "bus_frames": [ + "24 30 34 42 32 30 31 39 30 30 31 0d", + "24 32 34 0d", + "24 32 34 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/who25-messages.tsv", + "suite": "who25-messages", + "suite_sha256": "57f36863e445aa963e48f4bfcd45297256dab5a2842c6b3c9944e66a845f7bb5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*25*21*0001##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 24, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*25*22*0001##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 25, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*25*23#8*233##": [ + { + "bus_frames": [ + "24 30 34 42 32 32 31 39 32 30 38 0d", + "24 32 34 0d", + "24 32 34 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/who25-messages.tsv", + "suite": "who25-messages", + "suite_sha256": "57f36863e445aa963e48f4bfcd45297256dab5a2842c6b3c9944e66a845f7bb5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*25*23*0001##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 26, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/matrix-refused.tsv", + "suite": "matrix-refused", + "suite_sha256": "688eb035af81e718cc32afaa824bb19d94e6e84542ed989ee8f31813bb410c40", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*25*31#1*339##": [ + { + "bus_frames": [ + "24 30 34 42 34 33 39 39 32 30 30 0d", + "24 32 34 0d", + "24 32 34 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/who25-messages.tsv", + "suite": "who25-messages", + "suite_sha256": "57f36863e445aa963e48f4bfcd45297256dab5a2842c6b3c9944e66a845f7bb5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*25*32#1*33##": [ + { + "bus_frames": [ + "24 30 34 42 34 30 33 39 32 30 31 0d", + "24 32 34 0d", + "24 32 34 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 21, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/who25-messages.tsv", + "suite": "who25-messages", + "suite_sha256": "57f36863e445aa963e48f4bfcd45297256dab5a2842c6b3c9944e66a845f7bb5", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "out", + "version": "010108" + } + ], + "*4*0*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 31 46 31 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 20, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*0*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 31 46 31 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 21, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*0*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 16, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-zone-mode.tsv", + "suite": "thermo-zone-mode", + "suite_sha256": "46cfe1d80f81ff4fec7f38e5d9bd5d2d6a2060cd9d6e9f2e167f0d666252fa5f", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*4*1*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 31 46 30 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 22, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*1*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 31 46 30 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 23, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*1*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 17, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-zone-mode.tsv", + "suite": "thermo-zone-mode", + "suite_sha256": "46cfe1d80f81ff4fec7f38e5d9bd5d2d6a2060cd9d6e9f2e167f0d666252fa5f", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + } + ], + "*4*100*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 24, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*4*100*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 25, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*4*101*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 21, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 26, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*4*101*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 27, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*4*102*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 22, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 31 31 37 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 28, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*102*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 23, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 31 31 37 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 29, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*103*#0##": [ + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 31 31 36 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 30, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*103*#0#2##": [ + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 31 31 36 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 31, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*110*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 24, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 32, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*4*110*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 33, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*4*111*#0##": [ + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 32 30 30 31 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 34, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*111*#0#2##": [ + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 32 30 30 31 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 35, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*202*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 25, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 31 32 37 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 36, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*202*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 26, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 31 32 37 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 37, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*303*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 27, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 31 30 36 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 38, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*303*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 28, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 31 30 36 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 39, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*303*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 18, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-zone-mode.tsv", + "suite": "thermo-zone-mode", + "suite_sha256": "46cfe1d80f81ff4fec7f38e5d9bd5d2d6a2060cd9d6e9f2e167f0d666252fa5f", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 34 39 33 30 31 33 30 30 46 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 40, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*303*99##": [ + { + "bus_frames": [ + "24 30 34 39 33 36 33 33 30 30 46 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 41, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*311*#0##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 29, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 32 30 30 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 42, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*311*#0#2##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 30, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-central-mode.tsv", + "suite": "thermo-central-mode", + "suite_sha256": "c2a003ea3e00d05ee10a22143f2bf83522f987c2491900449d1f8b07a76d57d7", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 32 30 33 30 32 43 32 30 30 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 43, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*311*#1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 19, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-zone-mode.tsv", + "suite": "thermo-zone-mode", + "suite_sha256": "46cfe1d80f81ff4fec7f38e5d9bd5d2d6a2060cd9d6e9f2e167f0d666252fa5f", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 32 30 31 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 44, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*311*#99##": [ + { + "bus_frames": [ + "24 30 36 44 31 30 30 30 33 30 32 43 32 36 33 30 30 30 30 0d" + ], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 45, + "product": "MyHomeServer1", + "reply": "ack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "out", + "version": "028206" + } + ], + "*4*311*1##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "e32d3404302f3619bbbb564ae50554ab81ac6ef818b6e5a2290142a5e2ab5fb3", + "line_number": 20, + "product": "MH200N", + "reply": "nack", + "source_tsv": "results/MH200N/010108/oracle/full/thermo-zone-mode.tsv", + "suite": "thermo-zone-mode", + "suite_sha256": "46cfe1d80f81ff4fec7f38e5d9bd5d2d6a2060cd9d6e9f2e167f0d666252fa5f", + "target_sha256": "4ac640408201240096b09374522a7130967ad074047aa3a0aa455835fd737301", + "verdict": "silent", + "version": "010108" + }, + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 46, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ], + "*4*311*99##": [ + { + "bus_frames": [], + "direction": "down", + "emitted_own": [], + "image_sha256": "aa1cd393e4ae4262b3b2c4f9f25b8e831adbed648975505e2084c801f2b7f5e7", + "line_number": 47, + "product": "MyHomeServer1", + "reply": "nack", + "source_tsv": "results/MyHomeServer1/028206/oracle/full/ownd-pr82-heating.tsv", + "suite": "ownd-pr82-heating", + "suite_sha256": "adad16dc49a8fc39f23205212bc6365ec702326f5e201764d33b2095fa80f27a", + "target_sha256": "5d511f9f848f2ec07d3d14bf9a1084de4084a8b976fb9aece332c7aa9bd3a856", + "verdict": "silent", + "version": "028206" + } + ] + }, + "verdicts_sha256": "53df92a0f3f29beac2a84267163779a9b0415a2cc05e28921526a51cdd517c66" +} diff --git a/src/openwebnet_mcp/firmware_oracle.py b/src/openwebnet_mcp/firmware_oracle.py new file mode 100644 index 0000000..c0d341e --- /dev/null +++ b/src/openwebnet_mcp/firmware_oracle.py @@ -0,0 +1,305 @@ +"""Empirical firmware oracle index loader and verdict resolver for OpenWebNet frames. + +Loads results/mcp_index.json generated by own-firmware-oracle, which records exact +client session responses, bus telegram emissions, and SHA-256 provenance from +live QEMU firmware execution on BTicino MH200N and MyHomeServer1 gateways. +""" + +from __future__ import annotations + +import hashlib +import json +import logging +from pathlib import Path +from typing import Any + +from openwebnet_mcp._paths import resolve_oracle_index + +logger = logging.getLogger("openwebnet_mcp.firmware_oracle") + + +def normalize_frame(frame: str) -> str: + """Normalize OpenWebNet frame string for dictionary lookup.""" + f = frame.strip() + if f and not f.startswith("*"): + f = "*" + f + if f and not f.endswith("##"): + f = f + "##" + return f + + +class FirmwareOracle: + """In-memory loader and query engine for empirical firmware execution verdicts.""" + + def __init__(self, index_path: Path | None = None) -> None: + self.explicit_path = index_path + self.path: Path | None = None + self.source_tier: str = "none" + self.loaded: bool = False + self.load_error: str | None = None + self.generator: str | None = None + self.format_version: str | None = None + self.schema_version: str | None = None + self.total_unique_inputs: int = 0 + self.verdicts_sha256: str | None = None + self.computed_sha256: str | None = None + self.hash_verified: bool | None = None + self.gateways: list[dict[str, Any]] = [] + self.verdicts: dict[str, list[dict[str, Any]]] = {} + + def load(self) -> bool: + """Load or reload the oracle index from disk. Returns True on success.""" + if self.explicit_path: + resolved = self.explicit_path + tier = "explicit" + else: + resolved, tier = resolve_oracle_index() + + self.source_tier = tier + + if not resolved or not resolved.is_file(): + self.loaded = False + self.load_error = f"Oracle index not found (resolved: {resolved})" + logger.debug(self.load_error) + return False + + self.path = resolved + try: + with open(resolved, "r", encoding="utf-8") as f: + data = json.load(f) + + self.generator = data.get("generator") + self.format_version = data.get("format_version") + self.schema_version = data.get("schema_version") + if self.format_version and not self.format_version.startswith("1."): + logger.warning( + "Unexpected oracle index format_version '%s' at %s (expected 1.x)", + self.format_version, + resolved, + ) + + self.total_unique_inputs = int(data.get("total_unique_inputs", 0)) + self.gateways = data.get("gateways", []) + self.verdicts = data.get("verdicts", {}) + self.verdicts_sha256 = data.get("verdicts_sha256") + + # Cryptographic canonical hash verification + if self.verdicts_sha256 and self.verdicts: + canonical_json = json.dumps( + self.verdicts, sort_keys=True, separators=(",", ":") + ) + self.computed_sha256 = hashlib.sha256( + canonical_json.encode("utf-8") + ).hexdigest() + self.hash_verified = self.computed_sha256 == self.verdicts_sha256 + if not self.hash_verified: + logger.warning( + "Oracle index hash mismatch at %s (expected: %s, computed: %s)", + resolved, + self.verdicts_sha256, + self.computed_sha256, + ) + else: + self.computed_sha256 = None + self.hash_verified = None + + self.loaded = True + self.load_error = None + logger.info( + "Loaded firmware oracle index from %s [tier: %s] (%d inputs, %d gateways, hash: %s)", + resolved, + self.source_tier, + len(self.verdicts), + len(self.gateways), + "verified" if self.hash_verified else ("mismatch" if self.hash_verified is False else "none"), + ) + return True + except Exception as err: + self.loaded = False + self.load_error = f"Failed to parse oracle index {resolved}: {err}" + logger.warning(self.load_error, exc_info=True) + return False + + def reload(self) -> bool: + """Force reload from disk.""" + return self.load() + + def lookup(self, frame: str) -> list[dict[str, Any]] | None: + """Look up empirical verdicts for an OpenWebNet frame. + + Returns a list of verdict dicts across tested gateways, or None if not indexed. + """ + if not self.loaded: + self.load() + + if not self.loaded: + return None + + # Try exact string + if frame in self.verdicts: + return self.verdicts[frame] + + # Try normalized + norm = normalize_frame(frame) + if norm in self.verdicts: + return self.verdicts[norm] + + return None + + def format_verdict_markdown( + self, + frame: str, + verdicts: list[dict[str, Any]] | None, + ) -> str: + """Format empirical verdicts into a GitHub-flavored Markdown section.""" + norm = normalize_frame(frame) + + if not self.loaded: + err_msg = self.load_error or "Oracle index could not be loaded" + return ( + "## 🛡️ Empirical Firmware Oracle Verification\n" + f"*⚠️ Oracle index unavailable: {err_msg}.*" + ) + + if not verdicts: + return ( + "## 🛡️ Empirical Firmware Oracle Verification\n" + f"*Frame `{norm}` has not yet been replayed against firmware test suites " + "(MyHomeServer1 `028206` / MH200N `010108`).*" + ) + + normalized_note = "" + if frame.strip() != norm: + normalized_note = f" *(Matched normalized frame `{norm}`)*" + + lines = [ + f"## 🛡️ Empirical Firmware Oracle Verification: `{norm}`{normalized_note}", + "", + "| Gateway | Firmware | Reply | Verdict | Bus Frames Emitted | Emitted OWN | Suite | Target SHA |", + "|---|---|---|---|---|---|---|---|", + ] + + for v in verdicts: + gw = v.get("product", "Unknown") + ver = v.get("version", "Unknown") + raw_reply = str(v.get("reply", "silent")).strip().lower() + if raw_reply == "ack": + reply = "`ACK (*#*1##)`" + elif raw_reply == "nack": + reply = "`NACK (*#*0##)`" + elif raw_reply == "silent": + reply = "`SILENT`" + elif raw_reply == "-": + reply = "*No client reply*" + else: + reply = f"`{raw_reply}`" + + raw_verdict = str(v.get("verdict", "unknown")).lower() + if raw_verdict == "out": + verdict_badge = "`OUT (Bus)`" + elif raw_verdict == "silent": + verdict_badge = "`SILENT (Drop)`" + else: + verdict_badge = f"`{raw_verdict}`" + + bus = v.get("bus_frames") or [] + bus_str = f"`{', '.join(bus)}`" if bus else "*(none)*" + + emitted = v.get("emitted_own") or [] + emitted_str = f"`{', '.join(emitted)}`" if emitted else "*(none)*" + + suite = v.get("suite", "unknown") + target_sha = (v.get("target_sha256") or "")[:12] + sha_str = f"`{target_sha}`" if target_sha else "*(n/a)*" + + lines.append( + f"| **{gw}** | `{ver}` | {reply} | {verdict_badge} | {bus_str} | {emitted_str} | `{suite}` | {sha_str} |" + ) + + lines.extend([ + "", + "### 🔬 Ground Truth Analysis", + ]) + + for v in verdicts: + gw = v.get("product", "Unknown") + ver = v.get("version", "Unknown") + reply = str(v.get("reply", "silent")).strip().lower() + verdict = str(v.get("verdict", "unknown")).strip().lower() + bus = v.get("bus_frames") or [] + emitted = v.get("emitted_own") or [] + suite = v.get("suite", "unknown") + src = v.get("source_tsv", "") + + details = [f"- **{gw} ({ver})**:"] + if reply == "ack": + details.append("Gateway acknowledged the command (`*#*1##`).") + elif reply == "nack": + details.append("Gateway refused/rejected the command (`*#*0##`).") + elif reply == "-": + details.append("Gateway recorded no client session reply.") + elif reply == "silent": + details.append("Gateway was silent on the client session.") + else: + details.append(f"Gateway returned: `{reply}`.") + + if verdict == "out": + details.append( + f"Dispatched {len(bus)} SCS bus telegram(s): `{', '.join(bus)}` (physical bus transmission confirmed)." + ) + elif verdict == "silent": + details.append("Frame consumed or dropped internally; no SCS bus telegrams emitted.") + elif bus: + details.append(f"Dispatched {len(bus)} SCS bus telegram(s): `{', '.join(bus)}`.") + else: + details.append("No SCS bus telegrams emitted.") + + if emitted: + details.append(f"Emitted session responses: `{', '.join(emitted)}`.") + + if src: + details.append(f"Provenance: suite `{suite}` in `{src}`.") + + lines.append(" ".join(details)) + + # Integrity & provenance footer + hash_status_text = ( + "verified" + if self.hash_verified is True + else ("MISMATCH" if self.hash_verified is False else "unverified") + ) + source_name = self.path.name if self.path else "index.json" + lines.extend([ + "", + f"*(Oracle provenance: {self.source_tier} index `{source_name}`, SHA-256: {hash_status_text})*", + ]) + + return "\n".join(lines) + + def status(self) -> dict[str, Any]: + """Return diagnostic status information.""" + if not self.loaded: + self.load() + + return { + "loaded": self.loaded, + "path": str(self.path) if self.path else None, + "source_tier": self.source_tier, + "generator": self.generator, + "format_version": self.format_version, + "schema_version": self.schema_version, + "total_unique_inputs": self.total_unique_inputs, + "total_verdicts": len(self.verdicts), + "verdicts_sha256": self.verdicts_sha256, + "computed_sha256": self.computed_sha256, + "hash_verified": self.hash_verified, + "gateways": [ + { + "product": g.get("product"), + "version": g.get("version"), + "suites_count": len(g.get("suites", [])), + } + for g in self.gateways + ], + "load_error": self.load_error, + } diff --git a/src/openwebnet_mcp/rescan_manager.py b/src/openwebnet_mcp/rescan_manager.py index 71ce6cc..82e0229 100644 --- a/src/openwebnet_mcp/rescan_manager.py +++ b/src/openwebnet_mcp/rescan_manager.py @@ -3,13 +3,16 @@ from __future__ import annotations import logging -from typing import Any +from typing import TYPE_CHECKING, Any from openwebnet_mcp.ast_indexer import AstIndexer from openwebnet_mcp.doc_indexer import DocIndexer from openwebnet_mcp.kb import MachineKB from openwebnet_mcp.who_catalog import WhoCatalog +if TYPE_CHECKING: + from openwebnet_mcp.firmware_oracle import FirmwareOracle + logger = logging.getLogger("openwebnet_mcp.rescan_manager") @@ -22,11 +25,13 @@ def __init__( doc_indexer: DocIndexer, ast_indexer: AstIndexer, kb: MachineKB | None = None, + oracle: FirmwareOracle | None = None, ) -> None: self.who_catalog = who_catalog self.doc_indexer = doc_indexer self.ast_indexer = ast_indexer self.kb = kb + self.oracle = oracle def rescan(self) -> dict[str, Any]: """Perform a complete rescan and return status statistics.""" @@ -41,6 +46,7 @@ def rescan(self) -> dict[str, Any]: doc_sections_after = self.doc_indexer.reindex() ast_symbols_after = self.ast_indexer.reindex() kb_loaded = self.kb.load() if self.kb is not None else False + oracle_loaded = self.oracle.reload() if self.oracle is not None else False summary = { "who_families_loaded": len(self.who_catalog._families), @@ -59,6 +65,10 @@ def rescan(self) -> dict[str, Any]: "chunks": len(self.kb.chunks) if self.kb is not None else 0, "claims": len(self.kb.claims) if self.kb is not None else 0, }, + "oracle": { + "loaded": oracle_loaded, + "verdicts": len(self.oracle.verdicts) if (self.oracle is not None and self.oracle.loaded) else 0, + }, "status": "success", } logger.info("Rescan completed successfully: %s", summary) diff --git a/src/openwebnet_mcp/server.py b/src/openwebnet_mcp/server.py index 947a84a..4681738 100644 --- a/src/openwebnet_mcp/server.py +++ b/src/openwebnet_mcp/server.py @@ -32,6 +32,7 @@ from openwebnet_mcp._paths import get_log_dir, get_protocol_grammar_path from openwebnet_mcp.ast_indexer import AstIndexer from openwebnet_mcp.doc_indexer import DocIndexer +from openwebnet_mcp.firmware_oracle import FirmwareOracle from openwebnet_mcp.frame_generator import FrameGenerator from openwebnet_mcp.frame_parser import FrameParser from openwebnet_mcp.kb import MachineKB @@ -126,6 +127,7 @@ def clear(self): _ast_indexer: AstIndexer | None = None _rescan_manager: RescanManager | None = None _kb: MachineKB | None = None +_oracle: FirmwareOracle | None = None _catalog_lock = asyncio.Lock() _doc_lock = asyncio.Lock() @@ -138,6 +140,7 @@ def clear(self): _frame_syntax_cache = AsyncTTLCache("frame_syntax", ttl_seconds=600) _ast_cache = AsyncTTLCache("ast_symbol", ttl_seconds=600) _kb_cache = AsyncTTLCache("kb", ttl_seconds=600) +_oracle_cache = AsyncTTLCache("oracle_verdict", ttl_seconds=600) def _get_catalog() -> WhoCatalog: @@ -185,6 +188,16 @@ def _get_kb() -> MachineKB: return _kb +def _get_oracle() -> FirmwareOracle: + """Return the Empirical Firmware Oracle, loading it on first use.""" + global _oracle + if _oracle is None: + _oracle = FirmwareOracle() + if not _oracle.loaded: + _oracle.load() + return _oracle + + def _get_rescan_manager() -> RescanManager: global _rescan_manager if _rescan_manager is None: @@ -193,6 +206,7 @@ def _get_rescan_manager() -> RescanManager: doc_indexer=_get_doc_indexer(), ast_indexer=_get_ast_indexer(), kb=_get_kb(), + oracle=_get_oracle(), ) return _rescan_manager @@ -402,13 +416,51 @@ async def parse_and_validate_frame(frame: str) -> str: for w in parsed.warnings: lines.append(f"- {w}") + # Empirical Firmware Oracle Verification + try: + oracle = _get_oracle() + verdicts = oracle.lookup(frame) + lines.extend(["", oracle.format_verdict_markdown(frame, verdicts)]) + except Exception as oracle_err: + logger.warning("Empirical oracle lookup failed for frame '%s': %s", frame, oracle_err) + lines.extend([ + "", + "## 🛡️ Empirical Firmware Oracle Verification", + f"*⚠️ Empirical oracle lookup error: {oracle_err}*", + ]) + return "\n".join(lines) except Exception as err: logger.error("parse_and_validate_frame failed: %s", err, exc_info=True) return f"**Error**: {err}" -# ── 6b. Draft sound source selection ───────────────────────────────── +# ── 6b. Lookup Firmware Verdict ────────────────────────────────────── +@mcp.tool() +async def lookup_firmware_verdict(frame: str) -> str: + """Look up empirical gateway firmware behavior (MH200N 010108, MyHomeServer1 028206) for an OpenWebNet frame. + + Queries the hash-pinned oracle database from live QEMU firmware execution to return + exact gateway session responses (ACK/NACK), bus hex telegrams emitted, and SHA-256 provenance. + + Args: + frame: OpenWebNet frame to check (e.g. '*1*1*12##', '*#4*1*0##', '*1001*19*0##'). + """ + key = frame.strip() + + async def _impl(): + oracle = _get_oracle() + verdicts = oracle.lookup(key) + return oracle.format_verdict_markdown(key, verdicts) + + try: + return await _oracle_cache.get_or_set(key, _impl) + except Exception as err: + logger.error("lookup_firmware_verdict failed: %s", err, exc_info=True) + return f"**Error**: {err}" + + +# ── 6c. Draft sound source selection ───────────────────────────────── @mcp.tool() async def draft_sound_source_selection( amplifier: str, @@ -606,6 +658,7 @@ async def rescan_documentation() -> str: _frame_syntax_cache.clear() _ast_cache.clear() _kb_cache.clear() + _oracle_cache.clear() mgr = _get_rescan_manager() summary = mgr.rescan() @@ -733,6 +786,13 @@ async def resource_kb_manifest() -> str: return json.dumps(kb.manifest, indent=2, sort_keys=True) +@mcp.resource("oracle://index") +async def resource_oracle_index() -> str: + """Read-only diagnostic summary of the empirical firmware oracle index.""" + oracle = _get_oracle() + return json.dumps(oracle.status(), indent=2, sort_keys=True) + + @mcp.resource("docs://toc") async def resource_docs_toc() -> str: """Read-only table of contents for OpenWebNet & MyHOME documentation.""" @@ -795,7 +855,7 @@ def prompt_boost(topic: str = "general") -> str: ## Recommended Agent Workflow 1. Use `search_documentation(query)` or `get_ha_guide(topic)` to verify installation and configuration requirements. 2. Use `get_who_spec(who)` or `lookup_frame_syntax(frame_type, who)` to verify exact commands and dimension registers. -3. Use `parse_and_validate_frame(frame)` to verify frame syntax, parameter definitions, and semantic meanings. +3. Use `parse_and_validate_frame(frame)` to verify frame syntax, parameter definitions, semantic meanings, and empirical firmware oracle replay results (or `lookup_firmware_verdict(frame)` for dedicated QEMU provenance). 4. Use `draft_own_frame(...)` and `draft_ha_config(...)` to build copy-pasteable frames and Home Assistant configurations. 5. Use `get_code_signature(symbol)` to inspect Python classes and methods in `custom_components/myhome` or `OWNd`. 6. Use `search_knowledge(query)` / `get_knowledge_record(id)` for evidence-qualified answers (epistemic status, applicability, cautions, open questions); treat `unresolved`, `inferred`, `rejected` and `superseded` records as not established, and absence of a record as not a negative assertion. diff --git a/tests/test_firmware_oracle.py b/tests/test_firmware_oracle.py new file mode 100644 index 0000000..3addf1a --- /dev/null +++ b/tests/test_firmware_oracle.py @@ -0,0 +1,258 @@ +"""Unit tests for FirmwareOracle loader, normalization, verdict formatting, and status.""" + +import hashlib +import json +from pathlib import Path +import pytest + +from openwebnet_mcp.firmware_oracle import FirmwareOracle, normalize_frame + + +def test_normalize_frame(): + assert normalize_frame("*1*1*12##") == "*1*1*12##" + assert normalize_frame("1*1*12") == "*1*1*12##" + assert normalize_frame("1*1*12##") == "*1*1*12##" + assert normalize_frame("*1*1*12") == "*1*1*12##" + assert normalize_frame(" *1*1*12## \n") == "*1*1*12##" + assert normalize_frame("") == "" + + +def test_oracle_init_defaults(): + oracle = FirmwareOracle() + assert oracle.loaded is False + assert oracle.load_error is None + assert oracle.generator is None + assert oracle.total_unique_inputs == 0 + assert oracle.verdicts == {} + assert oracle.gateways == [] + assert oracle.source_tier == "none" + assert oracle.hash_verified is None + + +def test_oracle_load_default(): + oracle = FirmwareOracle() + success = oracle.load() + assert success is True + assert oracle.loaded is True + assert oracle.load_error is None + assert oracle.total_unique_inputs > 0 + assert len(oracle.verdicts) > 0 + assert len(oracle.gateways) >= 2 + assert oracle.generator == "own-firmware-oracle" + assert oracle.source_tier in ("bundled", "sibling") + assert oracle.verdicts_sha256 is not None + assert oracle.hash_verified is True + assert oracle.computed_sha256 == oracle.verdicts_sha256 + + +def test_oracle_lookup_and_normalization(): + oracle = FirmwareOracle() + oracle.load() + + # Known frame in index + v1 = oracle.lookup("*#1*74##") + assert v1 is not None + assert len(v1) >= 1 + assert v1[0]["product"] in ("MH200N", "MyHomeServer1") + + # Unnormalized / stripped lookup + v2 = oracle.lookup(" #1*74 ") + assert v2 == v1 + + # Missing frame + v_missing = oracle.lookup("*999*999*999##") + assert v_missing is None + + +def test_format_verdict_markdown_unverified(): + oracle = FirmwareOracle() + oracle.load() + md = oracle.format_verdict_markdown("*999*999##", None) + assert "Empirical Firmware Oracle Verification" in md + assert "has not yet been replayed" in md + assert "028206" in md + assert "010108" in md + assert "*999*999##" in md + + # Also with empty list + md_empty = oracle.format_verdict_markdown("*999*999##", []) + assert "has not yet been replayed" in md_empty + + +def test_format_verdict_markdown_load_failed(tmp_path): + missing_file = tmp_path / "nonexistent.json" + oracle = FirmwareOracle(index_path=missing_file) + oracle.load() + md = oracle.format_verdict_markdown("*1*1*1##", None) + assert "Empirical Firmware Oracle Verification" in md + assert "Oracle index unavailable:" in md + + +def test_format_verdict_markdown_various_verdicts(): + oracle = FirmwareOracle() + oracle.load() + sample_verdicts = [ + { + "product": "MH200N", + "version": "010108", + "reply": "ack", + "verdict": "out", + "bus_frames": ["12 34 56 0d"], + "emitted_own": ["*#*1##"], + "suite": "suite_lighting", + "target_sha256": "abcdef0123456789abcdef0123456789abcdef01", + "source_tsv": "results/MH200N/010108/oracle/lighting.tsv", + }, + { + "product": "MyHomeServer1", + "version": "028206", + "reply": "nack", + "verdict": "silent", + "bus_frames": [], + "emitted_own": [], + "suite": "suite_climate", + "target_sha256": None, + "source_tsv": "", + }, + { + "product": "MH200N", + "version": "010108", + "reply": "-", + "verdict": "out", + "bus_frames": ["AA BB CC"], + "emitted_own": [], + "suite": "suite_no_reply", + "target_sha256": "", + "source_tsv": "", + }, + { + "product": "MH200N", + "version": "010108", + "reply": "silent", + "verdict": "silent", + "bus_frames": [], + "emitted_own": [], + "suite": "suite_silent", + "target_sha256": "", + "source_tsv": "", + }, + { + "product": "MH200N", + "version": "010108", + "reply": "custom_response", + "verdict": "custom", + "bus_frames": ["AA BB CC", "DD EE FF"], + "emitted_own": ["*1*1*1##"], + "suite": "suite_custom", + "target_sha256": "1234567890abcdef", + "source_tsv": "results/test.tsv", + }, + ] + + md = oracle.format_verdict_markdown("1*1*12", sample_verdicts) + assert "*(Matched normalized frame `*1*1*12##`)*" in md + assert "| Gateway | Firmware | Reply | Verdict | Bus Frames Emitted | Emitted OWN | Suite | Target SHA |" in md + assert "**MH200N**" in md + assert "`010108`" in md + assert "`028206`" in md + assert "`ACK (*#*1##)`" in md + assert "`NACK (*#*0##)`" in md + assert "*No client reply*" in md + assert "`SILENT`" in md + assert "`custom_response`" in md + assert "`OUT (Bus)`" in md + assert "`SILENT (Drop)`" in md + assert "Gateway recorded no client session reply." in md + assert "Dispatched 1 SCS bus telegram(s)" in md + assert "Dispatched 2 SCS bus telegram(s)" in md + assert "Frame consumed or dropped internally" in md + assert "Gateway acknowledged the command (`*#*1##`)." in md + assert "Gateway refused/rejected the command (`*#*0##`)." in md + assert "Gateway was silent on the client session." in md + assert "Gateway returned: `custom_response`." in md + assert "Provenance: suite `suite_lighting`" in md + assert "SHA-256: verified" in md + + + +def test_oracle_hash_mismatch(tmp_path, caplog): + # Create an index with a forged hash + tampered_index = tmp_path / "tampered_index.json" + data = { + "generator": "own-firmware-oracle", + "format_version": "1.0.0", + "schema_version": "1.0.0", + "verdicts_sha256": "0000000000000000000000000000000000000000000000000000000000000000", + "total_unique_inputs": 1, + "gateways": [{"product": "MH200N", "version": "010108"}], + "verdicts": {"*1*1*12##": [{"product": "MH200N", "version": "010108", "reply": "ack"}]}, + } + tampered_index.write_text(json.dumps(data), encoding="utf-8") + + oracle = FirmwareOracle(index_path=tampered_index) + assert oracle.load() is True + assert oracle.loaded is True + assert oracle.hash_verified is False + assert "Oracle index hash mismatch" in caplog.text + + md = oracle.format_verdict_markdown("*1*1*12##", oracle.lookup("*1*1*12##")) + assert "SHA-256: MISMATCH" in md + + + +def test_oracle_format_version_warning(tmp_path, caplog): + future_index = tmp_path / "future_index.json" + data = { + "generator": "own-firmware-oracle", + "format_version": "2.0.0", + "schema_version": "2.0.0", + "total_unique_inputs": 0, + "gateways": [], + "verdicts": {}, + } + future_index.write_text(json.dumps(data), encoding="utf-8") + + oracle = FirmwareOracle(index_path=future_index) + assert oracle.load() is True + assert "Unexpected oracle index format_version '2.0.0'" in caplog.text + + +def test_oracle_status(): + oracle = FirmwareOracle() + st = oracle.status() + assert st["loaded"] is True + assert st["generator"] == "own-firmware-oracle" + assert st["source_tier"] in ("bundled", "sibling") + assert st["total_unique_inputs"] > 0 + assert st["total_verdicts"] > 0 + assert st["verdicts_sha256"] is not None + assert st["hash_verified"] is True + assert len(st["gateways"]) >= 2 + assert "suites_count" in st["gateways"][0] + assert st["load_error"] is None + + +def test_oracle_reload(): + oracle = FirmwareOracle() + assert oracle.load() is True + assert oracle.reload() is True + assert oracle.loaded is True + + +def test_oracle_missing_file(tmp_path): + missing_file = tmp_path / "nonexistent_index.json" + oracle = FirmwareOracle(index_path=missing_file) + assert oracle.load() is False + assert oracle.loaded is False + assert "not found" in oracle.load_error + assert oracle.lookup("*1*1*12##") is None + + +def test_oracle_corrupt_file(tmp_path): + corrupt_file = tmp_path / "corrupt_index.json" + corrupt_file.write_text("{invalid json", encoding="utf-8") + oracle = FirmwareOracle(index_path=corrupt_file) + assert oracle.load() is False + assert oracle.loaded is False + assert "Failed to parse oracle index" in oracle.load_error + assert oracle.lookup("*1*1*12##") is None diff --git a/tests/test_paths.py b/tests/test_paths.py index 8e3da3c..11dc5eb 100644 --- a/tests/test_paths.py +++ b/tests/test_paths.py @@ -105,3 +105,56 @@ def test_ownd_repo_path(monkeypatch, tmp_path): m.setattr(_paths, "get_project_root", lambda: tmp_path / "dummy" / "dummy") assert _paths.get_ownd_repo_path() is None + +def test_oracle_index_path(monkeypatch, tmp_path, caplog): + fake_index = tmp_path / "custom_oracle.json" + fake_index.write_text("{}", encoding="utf-8") + monkeypatch.setenv("OPENWEBNET_ORACLE_INDEX", str(fake_index)) + + p, tier = _paths.resolve_oracle_index() + assert p == fake_index + assert tier == "env" + assert _paths.get_oracle_index_path() == fake_index + + # Non-existent env path logs warning and falls through + bad_env = tmp_path / "does_not_exist.json" + monkeypatch.setenv("OPENWEBNET_ORACLE_INDEX", str(bad_env)) + + # Sibling checkout + sibling_results = tmp_path / "own-firmware-oracle" / "results" + sibling_results.mkdir(parents=True) + sibling_file = sibling_results / "mcp_index.json" + sibling_file.write_text("{}", encoding="utf-8") + + with monkeypatch.context() as m: + m.setattr(_paths, "get_project_root", lambda: tmp_path / "openwebnet-mcp") + p_sib, tier_sib = _paths.resolve_oracle_index() + assert p_sib == sibling_file + assert tier_sib == "sibling" + assert _paths.get_oracle_index_path() == sibling_file + assert "OPENWEBNET_ORACLE_INDEX is set to" in caplog.text + + # Bundled fallback when sibling does not exist + bundled_dir = tmp_path / "data" + bundled_dir.mkdir(parents=True) + bundled_file = bundled_dir / "oracle_index.json" + bundled_file.write_text("{}", encoding="utf-8") + + with monkeypatch.context() as m: + m.setattr(_paths, "get_project_root", lambda: tmp_path / "empty" / "repo") + m.setattr(_paths, "get_data_dir", lambda: bundled_dir) + p_bun, tier_bun = _paths.resolve_oracle_index() + assert p_bun == bundled_file + assert tier_bun == "bundled" + assert _paths.get_oracle_index_path() == bundled_file + + # Neither exists -> returns (None, "none") + with monkeypatch.context() as m: + m.setattr(_paths, "get_project_root", lambda: tmp_path / "empty" / "repo") + m.setattr(_paths, "get_data_dir", lambda: tmp_path / "no_data") + p_none, tier_none = _paths.resolve_oracle_index() + assert p_none is None + assert tier_none == "none" + assert _paths.get_oracle_index_path() is None + + diff --git a/tests/test_rescan_manager.py b/tests/test_rescan_manager.py index cff026c..9f7bdd5 100644 --- a/tests/test_rescan_manager.py +++ b/tests/test_rescan_manager.py @@ -3,6 +3,8 @@ import pytest from openwebnet_mcp.ast_indexer import AstIndexer from openwebnet_mcp.doc_indexer import DocIndexer +from openwebnet_mcp.firmware_oracle import FirmwareOracle +from openwebnet_mcp.kb import MachineKB from openwebnet_mcp.rescan_manager import RescanManager from openwebnet_mcp.who_catalog import WhoCatalog @@ -11,11 +13,16 @@ def test_rescan_manager_execution(): cat = WhoCatalog() doc_idx = DocIndexer() ast_idx = AstIndexer() + kb = MachineKB() + oracle = FirmwareOracle() - manager = RescanManager(cat, doc_idx, ast_idx) + manager = RescanManager(cat, doc_idx, ast_idx, kb=kb, oracle=oracle) res = manager.rescan() assert res["status"] == "success" assert res["who_families_loaded"] >= 20 assert res["doc_sections"]["after"] > 0 assert res["ast_symbols"]["after"] > 0 + assert "oracle" in res + assert res["oracle"]["loaded"] is True + assert res["oracle"]["verdicts"] > 0 diff --git a/tests/test_server.py b/tests/test_server.py index 8f8d1af..e103018 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -82,6 +82,7 @@ async def test_tool_parse_and_validate_frame(): res_light = await server.parse_and_validate_frame("*1*1*12##") assert "- **Valid Grammar**: `YES`" in res_light assert "Lighting: Turn ON" in res_light + assert "Empirical Firmware Oracle Verification" in res_light # Valid climate frame res_climate = await server.parse_and_validate_frame("*#4*1*0*0215##") @@ -93,6 +94,31 @@ async def test_tool_parse_and_validate_frame(): assert "- **Valid Grammar**: `NO`" in res_invalid assert "Unrecognized OpenWebNet syntax" in res_invalid + # Oracle lookup error degrades gracefully without failing syntax parsing + with patch.object(server._get_oracle(), "lookup", side_effect=RuntimeError("Simulated oracle crash")): + res_oracle_fail = await server.parse_and_validate_frame("*1*1*12##") + assert "- **Valid Grammar**: `YES`" in res_oracle_fail + assert "Empirical oracle lookup error: Simulated oracle crash" in res_oracle_fail + + + +@pytest.mark.asyncio +async def test_tool_lookup_firmware_verdict(): + # Verified frame from index + res_hit = await server.lookup_firmware_verdict("*#1*74##") + assert "Empirical Firmware Oracle Verification" in res_hit + assert "MH200N" in res_hit + assert "Ground Truth Analysis" in res_hit + + # Cached hit + res_hit_cached = await server.lookup_firmware_verdict("*#1*74##") + assert res_hit == res_hit_cached + + # Unverified frame + res_miss = await server.lookup_firmware_verdict("*999*999##") + assert "Empirical Firmware Oracle Verification" in res_miss + assert "has not yet been replayed" in res_miss + @pytest.mark.asyncio async def test_tool_draft_own_frame(): @@ -139,9 +165,12 @@ async def test_tool_get_code_signature(): @pytest.mark.asyncio async def test_tool_rescan_documentation(): + server._oracle_cache.cache["*#1*74##"] = ("cached_value", 9999999999.0) res = await server.rescan_documentation() assert "# OpenWebNet Documentation Rescan Complete" in res assert "WHO Families Loaded" in res + assert "*#1*74##" not in server._oracle_cache.cache + @pytest.mark.asyncio @@ -166,6 +195,11 @@ async def test_resources(): res_unknown = await server.resource_guide("unknown_topic") assert "not found" in res_unknown + # Resource oracle-index + res_oracle = await server.resource_oracle_index() + assert "MH200N" in res_oracle + assert "total_unique_inputs" in res_oracle + def test_async_ttl_cache(): cache = server.AsyncTTLCache("test_cache", ttl_seconds=1, maxsize=2) @@ -231,6 +265,12 @@ async def test_server_tool_exceptions(monkeypatch): res = await server.parse_and_validate_frame("*1*1*12##") assert "**Error**" in res + # lookup_firmware_verdict error + with monkeypatch.context() as m: + m.setattr(server, "_get_oracle", lambda: (_ for _ in ()).throw(RuntimeError("Oracle error"))) + res = await server.lookup_firmware_verdict("*1*1*12##") + assert "**Error**" in res + # draft_own_frame error with monkeypatch.context() as m: m.setattr(server, "_get_generator", lambda: (_ for _ in ()).throw(RuntimeError("Gen error")))