{"record":{"id":"6c9f8f234e53c54c","repo":"mgth/LittleBigMouse","slug":"associate-a-hisense-vidaa-projector-first","errorCode":null,"errorMessage":"Associate a Hisense VIDAA projector first.","messagePattern":"Associate a Hisense VIDAA projector first\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp.Avalonia/HisenseVidaa/HisenseVidaaService.cs","lineNumber":195,"sourceCode":"        _store.Save(c);\n    }\n    /// <summary>\n    /// Slower burst than Tizen, and the trailing delay is kept: awaiting PowerOnAsync\n    /// therefore leaves the projector 150 ms to react before the caller reconnects.\n    /// </summary>\n    static readonly WakeOnLanOptions WakeOnLanBurst = new()\n    {\n        PacketCount = 3,\n        Port = 9,\n        DelayBetweenPackets = TimeSpan.FromMilliseconds(150),\n        DelayAfterLastPacket = true,\n    };\n\n    public Task PowerOnAsync(string id, CancellationToken ct = default)\n    {\n        var c=Required(id); if (string.IsNullOrWhiteSpace(c.MacAddress)) throw new InvalidOperationException(\"Enter the projector Wi-Fi MAC address first.\"); return WakeOnLan.SendAsync(c.MacAddress, WakeOnLanBurst, cancellationToken: ct);\n    }\n    HisenseVidaaConfiguration Required(string id) => _store.Get(id) is { } c && !string.IsNullOrWhiteSpace(c.IpAddress) ? c : throw new InvalidOperationException(\"Associate a Hisense VIDAA projector first.\");\n\n    async Task<HisenseVidaaClient> ClientForAsync(string id, HisenseVidaaConfiguration configuration)\n    {\n        HisenseVidaaClient? previous = null;\n        HisenseVidaaClient client;\n        lock (_clientLock)\n        {\n            if (_clients.TryGetValue(id, out client!)\n                && SameConnection(client.Configuration, configuration)) return client;\n\n            previous = client;\n            client = new HisenseVidaaClient(configuration);\n            _clients[id] = client;\n        }\n\n        if (previous is not null) await previous.DisposeAsync().ConfigureAwait(false);\n        return client;\n    }","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/mgth/LittleBigMouse/blob/7a42f01d47d99d223b8ee33ba4019af82adf1c48/LittleBigMouse.Plugins/LittleBigMouse.Plugin.Vcp.Avalonia/HisenseVidaa/HisenseVidaaService.cs#L177-L213","documentation":"HisenseVidaaService throws this InvalidOperationException from its Required() helper when a per-projector operation (e.g. PowerOnAsync and other commands) references a projector id whose stored configuration is missing or has no IP address. The service requires an associated (configured, IP-reachable) Hisense VIDAA projector before any device command can run. It is a precondition/state guard, not a network failure.","triggerScenarios":"Calling PowerOnAsync(id) (or any service method that calls Required(id)) when: the id is not present in _store; the stored HisenseVidaaConfiguration exists but IpAddress is null/empty/whitespace.","commonSituations":"User added the projector entry but never completed the association/pairing flow that sets the IP address; configuration was migrated or hand-edited and the IP field was lost; caller passes a stale id after the projector config was deleted; automation calls PowerOnAsync before the initial setup UI flow ran.","solutions":["Complete the projector association flow first so the configuration has a valid IpAddress, then retry the call.","Verify the projector id exists in the configuration store and that HisenseVidaaConfiguration.IpAddress is set before invoking service methods.","Fix or re-create the configuration entry if the IP was lost (hand-edited file, migration, reset).","In automation code, check the configuration for a non-empty IpAddress before calling service commands."],"exampleFix":"// before\nawait service.PowerOnAsync(projectorId);\n\n// after\nvar cfg = store.Get(projectorId);\nif (cfg is null || string.IsNullOrWhiteSpace(cfg.IpAddress))\n    throw new InvalidOperationException(\"Projector not associated: run the VIDAA association flow first.\");\nawait service.PowerOnAsync(projectorId);","handlingStrategy":"validation","validationCode":"var cfg = store.Get(projectorId);\nbool associated = cfg is not null && !string.IsNullOrWhiteSpace(cfg.IpAddress);\nif (!associated)\n    throw new InvalidOperationException(\"Associate the Hisense VIDAA projector (set its IP address) before issuing commands.\");","typeGuard":"static bool IsAssociated(HisenseVidaaConfiguration? c) =>\n    c is not null && !string.IsNullOrWhiteSpace(c.IpAddress);","tryCatchPattern":"try\n{\n    await service.PowerOnAsync(id);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Associate a Hisense VIDAA projector\"))\n{\n    // surface setup instructions to the user\n}","preventionTips":["Always run the association/pairing flow before automating projector commands.","Persist configurations through the store API instead of hand-editing files.","Guard automation entry points with an IsAssociated check on the stored config."],"tags":["csharp","state","configuration","precondition"],"backgroundTag":"invalid-state-transition","analyzedSha":"7a42f01d47d99d223b8ee33ba4019af82adf1c48","analyzedAt":"2026-09-16T00:35:00.514Z","contentChangedAt":"2026-09-16T00:35:00.514Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}