//! Parser for `core.device_registry` (HA storage schema v1, minor_version 1–13). //! //! P1: deserializes the envelope and returns `Vec`. //! HOMECORE's device registry isn't fully wired yet (ADR-127 §2.5 deferred //! to P2), so `DeviceImport` is a staging type for the future hand-off. use std::path::Path; use serde::{Deserialize, Serialize}; use crate::{storage::read_envelope, storage_format::v13, MigrateError}; /// Staging type for a device imported from HA. Not yet wired to HOMECORE's /// device registry (ADR-127 §2.5 — deferred to P2). #[derive(Clone, Debug, Serialize, Deserialize)] pub struct DeviceImport { pub id: String, pub config_entries: Vec, #[serde(default)] pub manufacturer: Option, #[serde(default)] pub model: Option, #[serde(default)] pub name: Option, /// `identifiers` — list of `[integration, id]` pairs. Preserved as raw /// JSON for P2 consumption; not yet mapped to HOMECORE DeviceEntry. #[serde(default)] pub identifiers: Vec>, #[serde(default)] pub connections: Vec>, #[serde(default)] pub via_device_id: Option, #[serde(default)] pub area_id: Option, } #[derive(Debug, Deserialize)] struct HaDeviceRegistryData { devices: Vec, /// Deleted device tombstones — ignored in P1. #[serde(default)] #[allow(dead_code)] deleted_devices: Vec, } /// Read `core.device_registry` from `path` and return the raw import list. pub fn read_device_registry(path: &Path) -> Result, MigrateError> { let env = read_envelope(path)?; let file_str = path.display().to_string(); v13::require_supported(&file_str, env.version, env.minor_version)?; let data: HaDeviceRegistryData = serde_json::from_value(env.data).map_err(|e| MigrateError::JsonParse { path: file_str, source: e, })?; Ok(data.devices) } #[cfg(test)] mod tests { use super::*; use std::io::Write; use tempfile::NamedTempFile; const FIXTURE: &str = r#"{ "version": 1, "minor_version": 13, "key": "core.device_registry", "data": { "devices": [ { "id": "dev_abc", "config_entries": ["ce_001"], "manufacturer": "Philips", "model": "Hue Bridge", "name": "Philips Hue Bridge", "identifiers": [["hue", "001788FFFE3D4B13"]], "connections": [["mac", "00:17:88:ff:fe:3d:4b:13"]], "via_device_id": null, "area_id": null } ], "deleted_devices": [] } }"#; #[test] fn parses_device_registry() { let mut f = NamedTempFile::new().unwrap(); f.write_all(FIXTURE.as_bytes()).unwrap(); let devices = read_device_registry(f.path()).unwrap(); assert_eq!(devices.len(), 1); let d = &devices[0]; assert_eq!(d.id, "dev_abc"); assert_eq!(d.manufacturer.as_deref(), Some("Philips")); assert_eq!(d.identifiers, vec![vec!["hue", "001788FFFE3D4B13"]]); } }