diff --git a/NightLight.Core.Tests/FakeHome.fs b/NightLight.Core.Tests/FakeHome.fs index 3c2471a..fc912fe 100644 --- a/NightLight.Core.Tests/FakeHome.fs +++ b/NightLight.Core.Tests/FakeHome.fs @@ -50,7 +50,7 @@ type FakeLight(light: Light) = if hasPower then brightness <- newBrightness - if light.Bulb = IkeaBulb then + if (lightProps light).Bulb = IkeaBulb then state <- true member _.SetColor(newColor: Color) = @@ -60,7 +60,7 @@ type FakeLight(light: Light) = type FakeHome() = let friendlyNameToFakeLight = lights - |> Seq.map (fun light -> light.FriendlyName, FakeLight light) + |> Seq.map (fun light -> (lightProps light).FriendlyName, FakeLight light) |> Map.ofSeq let onEventPublished = new Event() @@ -113,17 +113,17 @@ type FakeHome() = member _.Interact(interaction: Interaction) = match interaction with | HumanInteraction(LightPoweredOn light) -> - friendlyNameToFakeLight[light.FriendlyName].PowerOn() + friendlyNameToFakeLight[(lightProps light).FriendlyName].PowerOn() { Topic = "zigbee2mqtt/bridge/event" Payload = $@"{{ ""type"": ""device_announce"", - ""data"": {{ ""friendly_name"": ""{light.FriendlyName.Get}"" }} + ""data"": {{ ""friendly_name"": ""{(lightProps light).FriendlyName.Get}"" }} }}" } |> ReceivedZigbeeEvent |> onEventPublished.Trigger - | HumanInteraction(LightPoweredOff light) -> friendlyNameToFakeLight[light.FriendlyName].PowerOff() + | HumanInteraction(LightPoweredOff light) -> friendlyNameToFakeLight[(lightProps light).FriendlyName].PowerOff() | RemoteInteraction RemotePressedOnButton -> { Topic = $"zigbee2mqtt/{remoteControlFriendlyName.Get}" Payload = @"{ ""action"": ""on"" }" } @@ -149,15 +149,15 @@ type FakeHome with member this.NonRemotelyControlledLightStates = this.LightStates - |> Seq.filter (fst >> _.ControlledWithRemote >> (=) NonRemote) + |> Seq.filter (fst >> lightProps >> _.ControlledWithRemote >> (=) NonRemote) |> Seq.toList member this.RemotelyControlledLightStates = this.LightStates - |> Seq.filter (fst >> _.ControlledWithRemote >> (<>) NonRemote) + |> Seq.filter (fst >> lightProps >> _.ControlledWithRemote >> (<>) NonRemote) |> Seq.toList member this.Label = this.LightsThatAreOn - |> Seq.map (fun (light, state) -> $"{light.FriendlyName.Get}: {state}") + |> Seq.map (fun (light, state) -> $"{(lightProps light).FriendlyName.Get}: {state}") |> String.concat ", " diff --git a/NightLight.Core.Tests/LightArbitraries.fs b/NightLight.Core.Tests/LightArbitraries.fs index 2f7d2b4..6c36773 100644 --- a/NightLight.Core.Tests/LightArbitraries.fs +++ b/NightLight.Core.Tests/LightArbitraries.fs @@ -9,27 +9,27 @@ type ArbitraryLight = type ArbitraryNonRemotelyControlledLight = static member Light() = lights - |> Seq.filter _.ControlledWithRemote.IsNonRemote + |> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsNonRemote) |> Gen.elements |> Arb.fromGen type ArbitraryLeftRemotelyControlledLight = static member Light() = lights - |> Seq.filter _.ControlledWithRemote.IsRemoteLeft + |> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsRemoteLeft) |> Gen.elements |> Arb.fromGen type ArbitraryRightRemotelyControlledLight = static member Light() = lights - |> Seq.filter _.ControlledWithRemote.IsRemoteRight + |> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsRemoteRight) |> Gen.elements |> Arb.fromGen type ArbitraryRemotelyControlledLight = static member Light() = lights - |> Seq.filter (not << _.ControlledWithRemote.IsNonRemote) + |> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsNonRemote |> not) |> Gen.elements |> Arb.fromGen diff --git a/NightLight.Core.Tests/NightLightTests.fs b/NightLight.Core.Tests/NightLightTests.fs index 1dfc2ea..19e659a 100644 --- a/NightLight.Core.Tests/NightLightTests.fs +++ b/NightLight.Core.Tests/NightLightTests.fs @@ -52,7 +52,7 @@ type NightLightTests() = && time <= endOfAlarm let scaledForAlarm light brightness = - if light.ControlledWithRemote <> NonRemote && alarm then + if (lightProps light).ControlledWithRemote <> NonRemote && alarm then float brightness * ((time - startOfDay) / (endOfAlarm - startOfDay)) |> byte else brightness @@ -60,14 +60,14 @@ type NightLightTests() = fakeHome.LightStates |> Seq.forall (fun (light, state) -> let maybeExpectedBrightness = - match light, state with + match (lightProps light).Bulb, state with | _, Off -> None - | { Bulb = IkeaBulb }, On(_, White) -> Some 254uy - | { Bulb = IkeaBulb }, On(_, Yellow) -> Some 210uy - | { Bulb = IkeaBulb }, On(_, Red) -> Some 254uy - | { Bulb = PaulmannBulb }, On(_, White) -> Some 35uy - | { Bulb = PaulmannBulb }, On(_, Yellow) -> Some 35uy - | { Bulb = PaulmannBulb }, On(_, Red) -> Some 80uy + | IkeaBulb, On(_, White) -> Some 254uy + | IkeaBulb, On(_, Yellow) -> Some 210uy + | IkeaBulb, On(_, Red) -> Some 254uy + | PaulmannBulb, On(_, White) -> Some 35uy + | PaulmannBulb, On(_, Yellow) -> Some 35uy + | PaulmannBulb, On(_, Red) -> Some 80uy |> Option.map (scaledForAlarm light) let maybeActualBrightness = @@ -109,7 +109,7 @@ type NightLightTests() = let allOff (ls: (Light * LightState) seq) = ls |> Seq.forall (snd >> _.IsOff) let controlledBy remote ls = - ls |> Seq.filter (fst >> _.ControlledWithRemote >> (=) remote) + ls |> Seq.filter (fst >> lightProps >> _.ControlledWithRemote >> (=) remote) let maybeLastRemoteInteraction = tryGetLastRemoteInteraction interactions diff --git a/NightLight.Core/Models.fs b/NightLight.Core/Models.fs index 18e4ef3..6f260ce 100644 --- a/NightLight.Core/Models.fs +++ b/NightLight.Core/Models.fs @@ -46,32 +46,52 @@ type LightControl = | RemoteRight type Light = + | VardagsrumFonsterlampa + | VardagsrumVagglampa + | VardagsrumGolvlampa + | BadrumTaklampa + | SovrumNattduksbordlampa + +type LightProps = { FriendlyName: DeviceFriendlyName Room: Room Bulb: Bulb ControlledWithRemote: LightControl } +let lightProps light = + match light with + | VardagsrumFonsterlampa -> + { FriendlyName = DeviceFriendlyName "Vardagsrum - Fönsterlampa" + Room = Bedroom + Bulb = IkeaBulb + ControlledWithRemote = RemoteRight } + | VardagsrumVagglampa -> + { FriendlyName = DeviceFriendlyName "Vardagsrum - Vägglampa" + Room = LivingRoom + Bulb = PaulmannBulb + ControlledWithRemote = NonRemote } + | VardagsrumGolvlampa -> + { FriendlyName = DeviceFriendlyName "Vardagsrum - Golvlampa" + Room = LivingRoom + Bulb = PaulmannBulb + ControlledWithRemote = NonRemote } + | BadrumTaklampa -> + { FriendlyName = DeviceFriendlyName "Badrum - Taklampa" + Room = Bathroom + Bulb = IkeaBulb + ControlledWithRemote = NonRemote } + | SovrumNattduksbordlampa -> + { FriendlyName = DeviceFriendlyName "Sovrum - Nattduksbordlampa" + Room = Bedroom + Bulb = IkeaBulb + ControlledWithRemote = RemoteLeft } + let lights = - [ { FriendlyName = DeviceFriendlyName "Vardagsrum - Fönsterlampa" - Room = Bedroom - Bulb = IkeaBulb - ControlledWithRemote = RemoteRight } - { FriendlyName = DeviceFriendlyName "Vardagsrum - Vägglampa" - Room = LivingRoom - Bulb = PaulmannBulb - ControlledWithRemote = NonRemote } - { FriendlyName = DeviceFriendlyName "Vardagsrum - Golvlampa" - Room = LivingRoom - Bulb = PaulmannBulb - ControlledWithRemote = NonRemote } - { FriendlyName = DeviceFriendlyName "Badrum - Taklampa" - Room = Bathroom - Bulb = IkeaBulb - ControlledWithRemote = NonRemote } - { FriendlyName = DeviceFriendlyName "Sovrum - Nattduksbordlampa" - Room = Bedroom - Bulb = IkeaBulb - ControlledWithRemote = RemoteLeft } ] + [ VardagsrumFonsterlampa + VardagsrumVagglampa + VardagsrumGolvlampa + BadrumTaklampa + SovrumNattduksbordlampa ] let remoteControlFriendlyName = DeviceFriendlyName "Fjärrkontroll" diff --git a/NightLight.Core/NightLightStateMachine.fs b/NightLight.Core/NightLightStateMachine.fs index 11e56cf..95d2b51 100644 --- a/NightLight.Core/NightLightStateMachine.fs +++ b/NightLight.Core/NightLightStateMachine.fs @@ -9,14 +9,14 @@ open NightLight.Core.Moods open FsToolkit.ErrorHandling let internal tryFindLight friendlyName = - Seq.tryFind (fun light -> light.FriendlyName = friendlyName) lights + Seq.tryFind (fun light -> (lightProps light).FriendlyName = friendlyName) lights let internal generateZigbeeCommandsToFixLight (light: Light) (desiredLightState: LightState) = seq { - match light.ControlledWithRemote, desiredLightState.State with + match (lightProps light).ControlledWithRemote, desiredLightState.State with | NonRemote, On -> () | NonRemote, Off -> failwith $"Unexpectly trying to turn off {light}. It's not remote-controlled." - | _, On when light.Bulb = IkeaBulb -> () // Rely on the brightness command for turning it on + | _, On when (lightProps light).Bulb = IkeaBulb -> () // Rely on the brightness command for turning it on | _, _ -> yield generateStateCommand desiredLightState.State light if desiredLightState.State = On then @@ -40,7 +40,8 @@ let internal createOrUpdateNightLightState lights |> Seq.map (fun light -> let color, brightness = - getDesiredMood light.Room partOfDay |> getDesiredColorAndBrightness light.Bulb + getDesiredMood (lightProps light).Room partOfDay + |> getDesiredColorAndBrightness (lightProps light).Bulb let previousState = maybeOldLightToState @@ -50,7 +51,7 @@ let internal createOrUpdateNightLightState light, { Color = color Brightness = - if alarm && light.ControlledWithRemote <> NonRemote then + if alarm && (lightProps light).ControlledWithRemote <> NonRemote then brightness.Scale(getAlarmWeight time) else brightness @@ -72,7 +73,7 @@ let internal withStateFor (light: Light) (state: State) (oldNightLightState: Nig let internal withStateForRemoteControlledLights (state: State) (oldNightLightState: NightLightState) = lights - |> Seq.filter (not << _.ControlledWithRemote.IsNonRemote) + |> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsNonRemote |> not) |> Seq.fold (fun acc light -> acc |> withStateFor light state) oldNightLightState let internal withAlarmOff (oldNightLightState: NightLightState) = @@ -113,7 +114,8 @@ type NightLightStateMachine private (maybeState: NightLightState option) = | PressedOff -> currentState |> withAlarmOff |> withStateForRemoteControlledLights Off | PressedLeft -> let lightThatShouldBeOn = - lights |> Seq.find (fun light -> light.ControlledWithRemote = RemoteLeft) + lights + |> Seq.find (fun light -> (lightProps light).ControlledWithRemote = RemoteLeft) currentState |> withAlarmOff diff --git a/NightLight.Core/ZigbeeCommands.fs b/NightLight.Core/ZigbeeCommands.fs index 8c4dea2..dd13aff 100644 --- a/NightLight.Core/ZigbeeCommands.fs +++ b/NightLight.Core/ZigbeeCommands.fs @@ -4,7 +4,7 @@ open System.Text.Json.Nodes open NightLight.Core.Models let toZigbeeCommand light payload = - let topic = $"zigbee2mqtt/{light.FriendlyName.Get}/set" + let topic = $"zigbee2mqtt/{(lightProps light).FriendlyName.Get}/set" { Topic = topic; Payload = payload } let generateStateCommand state light = @@ -15,7 +15,7 @@ let generateStateCommand state light = | On -> "ON" | Off -> "OFF" - if light.Bulb = IkeaBulb then + if (lightProps light).Bulb = IkeaBulb then commandObj["transition"] <- 0 commandObj.ToJsonString() |> toZigbeeCommand light