diff --git a/Sources/KitchenSyncApp/Discovery/DeviceMatch.swift b/Sources/KitchenSyncApp/Discovery/DeviceMatch.swift index 459832b..bd973f4 100644 --- a/Sources/KitchenSyncApp/Discovery/DeviceMatch.swift +++ b/Sources/KitchenSyncApp/Discovery/DeviceMatch.swift @@ -30,11 +30,16 @@ enum DeviceMatch { /// units working, not because it is correct. /// /// The firmware advertises generic `_http._tcp` with no TXT record, so the hostname is all a - /// client has. `ks_hostname()` builds `-`: `kitchensync-a4f2` on - /// the P4, `kstouch-dfd0` on the Touch. Missing `kstouch` is what made a real device invisible. + /// client has. The hostname is `-`: `kitchensync-a4f2` on the + /// P4, `kstouch-dfd0` on the Touch, `x32link-7a1c` on the headless (ESP-035). Missing + /// `kstouch` is what made a real device invisible once already. private static func matchesKnownHostname(_ name: String) -> Bool { knownHostPrefixes.contains { name.hasPrefix($0) } } - private static let knownHostPrefixes = ["kitchensync", "kstouch"] + /// `x32link` joined this list in ESP-035 — but adding it here was the SMALL half. That + /// unit had no mDNS whatsoever: it never advertised, so no matcher could have found it. + /// The user saw it exactly that way — "I see kstouch, not the headless" — while the box + /// sat there powered, on the network, and following the session. + private static let knownHostPrefixes = ["kitchensync", "kstouch", "x32link"] } diff --git a/Tests/KitchenSyncAppTests/DeviceMatchTests.swift b/Tests/KitchenSyncAppTests/DeviceMatchTests.swift index 914ea1b..e7bf692 100644 --- a/Tests/KitchenSyncAppTests/DeviceMatchTests.swift +++ b/Tests/KitchenSyncAppTests/DeviceMatchTests.swift @@ -47,4 +47,21 @@ final class DeviceMatchTests: XCTestCase { txt: ["dev": "some-other-product"]), "a TXT record that says it ISN'T ours must beat a hostname that says it is") } + + /// ESP-035: the headless X32Link. It was the last unit in the fleet with NO mDNS at + /// all -- it never advertised, so it could not be found no matter what this matcher + /// did. Now it announces `x32link-`, and the prefix list has to know the name. + /// + /// The user hit this exactly: "I see kstouch. not the headless." The device was + /// powered, on the network, and following the Link session. + func test_the_headless_x32link_is_one_of_ours() { + XCTAssertTrue(DeviceMatch.isKitchenSync(serviceName: "x32link-7a1c", txt: nil)) + } + + /// The prefix is still a HEURISTIC, and it must not swallow the whole network. A + /// stranger's box at a venue is not ours. + func test_a_stranger_is_still_not_one_of_ours() { + XCTAssertFalse(DeviceMatch.isKitchenSync(serviceName: "brother-printer", txt: nil)) + XCTAssertFalse(DeviceMatch.isKitchenSync(serviceName: "x32-mixer", txt: nil)) + } }