Give every light its own type
This commit is contained in:
parent
dbb0389e61
commit
50c8a413a6
6 changed files with 72 additions and 50 deletions
|
|
@ -50,7 +50,7 @@ type FakeLight(light: Light) =
|
||||||
if hasPower then
|
if hasPower then
|
||||||
brightness <- newBrightness
|
brightness <- newBrightness
|
||||||
|
|
||||||
if light.Bulb = IkeaBulb then
|
if (lightProps light).Bulb = IkeaBulb then
|
||||||
state <- true
|
state <- true
|
||||||
|
|
||||||
member _.SetColor(newColor: Color) =
|
member _.SetColor(newColor: Color) =
|
||||||
|
|
@ -60,7 +60,7 @@ type FakeLight(light: Light) =
|
||||||
type FakeHome() =
|
type FakeHome() =
|
||||||
let friendlyNameToFakeLight =
|
let friendlyNameToFakeLight =
|
||||||
lights
|
lights
|
||||||
|> Seq.map (fun light -> light.FriendlyName, FakeLight light)
|
|> Seq.map (fun light -> (lightProps light).FriendlyName, FakeLight light)
|
||||||
|> Map.ofSeq
|
|> Map.ofSeq
|
||||||
|
|
||||||
let onEventPublished = new Event<Event>()
|
let onEventPublished = new Event<Event>()
|
||||||
|
|
@ -113,17 +113,17 @@ type FakeHome() =
|
||||||
member _.Interact(interaction: Interaction) =
|
member _.Interact(interaction: Interaction) =
|
||||||
match interaction with
|
match interaction with
|
||||||
| HumanInteraction(LightPoweredOn light) ->
|
| HumanInteraction(LightPoweredOn light) ->
|
||||||
friendlyNameToFakeLight[light.FriendlyName].PowerOn()
|
friendlyNameToFakeLight[(lightProps light).FriendlyName].PowerOn()
|
||||||
|
|
||||||
{ Topic = "zigbee2mqtt/bridge/event"
|
{ Topic = "zigbee2mqtt/bridge/event"
|
||||||
Payload =
|
Payload =
|
||||||
$@"{{
|
$@"{{
|
||||||
""type"": ""device_announce"",
|
""type"": ""device_announce"",
|
||||||
""data"": {{ ""friendly_name"": ""{light.FriendlyName.Get}"" }}
|
""data"": {{ ""friendly_name"": ""{(lightProps light).FriendlyName.Get}"" }}
|
||||||
}}" }
|
}}" }
|
||||||
|> ReceivedZigbeeEvent
|
|> ReceivedZigbeeEvent
|
||||||
|> onEventPublished.Trigger
|
|> onEventPublished.Trigger
|
||||||
| HumanInteraction(LightPoweredOff light) -> friendlyNameToFakeLight[light.FriendlyName].PowerOff()
|
| HumanInteraction(LightPoweredOff light) -> friendlyNameToFakeLight[(lightProps light).FriendlyName].PowerOff()
|
||||||
| RemoteInteraction RemotePressedOnButton ->
|
| RemoteInteraction RemotePressedOnButton ->
|
||||||
{ Topic = $"zigbee2mqtt/{remoteControlFriendlyName.Get}"
|
{ Topic = $"zigbee2mqtt/{remoteControlFriendlyName.Get}"
|
||||||
Payload = @"{ ""action"": ""on"" }" }
|
Payload = @"{ ""action"": ""on"" }" }
|
||||||
|
|
@ -149,15 +149,15 @@ type FakeHome with
|
||||||
|
|
||||||
member this.NonRemotelyControlledLightStates =
|
member this.NonRemotelyControlledLightStates =
|
||||||
this.LightStates
|
this.LightStates
|
||||||
|> Seq.filter (fst >> _.ControlledWithRemote >> (=) NonRemote)
|
|> Seq.filter (fst >> lightProps >> _.ControlledWithRemote >> (=) NonRemote)
|
||||||
|> Seq.toList
|
|> Seq.toList
|
||||||
|
|
||||||
member this.RemotelyControlledLightStates =
|
member this.RemotelyControlledLightStates =
|
||||||
this.LightStates
|
this.LightStates
|
||||||
|> Seq.filter (fst >> _.ControlledWithRemote >> (<>) NonRemote)
|
|> Seq.filter (fst >> lightProps >> _.ControlledWithRemote >> (<>) NonRemote)
|
||||||
|> Seq.toList
|
|> Seq.toList
|
||||||
|
|
||||||
member this.Label =
|
member this.Label =
|
||||||
this.LightsThatAreOn
|
this.LightsThatAreOn
|
||||||
|> Seq.map (fun (light, state) -> $"{light.FriendlyName.Get}: {state}")
|
|> Seq.map (fun (light, state) -> $"{(lightProps light).FriendlyName.Get}: {state}")
|
||||||
|> String.concat ", "
|
|> String.concat ", "
|
||||||
|
|
|
||||||
|
|
@ -9,27 +9,27 @@ type ArbitraryLight =
|
||||||
type ArbitraryNonRemotelyControlledLight =
|
type ArbitraryNonRemotelyControlledLight =
|
||||||
static member Light() =
|
static member Light() =
|
||||||
lights
|
lights
|
||||||
|> Seq.filter _.ControlledWithRemote.IsNonRemote
|
|> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsNonRemote)
|
||||||
|> Gen.elements
|
|> Gen.elements
|
||||||
|> Arb.fromGen
|
|> Arb.fromGen
|
||||||
|
|
||||||
type ArbitraryLeftRemotelyControlledLight =
|
type ArbitraryLeftRemotelyControlledLight =
|
||||||
static member Light() =
|
static member Light() =
|
||||||
lights
|
lights
|
||||||
|> Seq.filter _.ControlledWithRemote.IsRemoteLeft
|
|> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsRemoteLeft)
|
||||||
|> Gen.elements
|
|> Gen.elements
|
||||||
|> Arb.fromGen
|
|> Arb.fromGen
|
||||||
|
|
||||||
type ArbitraryRightRemotelyControlledLight =
|
type ArbitraryRightRemotelyControlledLight =
|
||||||
static member Light() =
|
static member Light() =
|
||||||
lights
|
lights
|
||||||
|> Seq.filter _.ControlledWithRemote.IsRemoteRight
|
|> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsRemoteRight)
|
||||||
|> Gen.elements
|
|> Gen.elements
|
||||||
|> Arb.fromGen
|
|> Arb.fromGen
|
||||||
|
|
||||||
type ArbitraryRemotelyControlledLight =
|
type ArbitraryRemotelyControlledLight =
|
||||||
static member Light() =
|
static member Light() =
|
||||||
lights
|
lights
|
||||||
|> Seq.filter (not << _.ControlledWithRemote.IsNonRemote)
|
|> Seq.filter (fun light -> (lightProps light).ControlledWithRemote.IsNonRemote |> not)
|
||||||
|> Gen.elements
|
|> Gen.elements
|
||||||
|> Arb.fromGen
|
|> Arb.fromGen
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ type NightLightTests() =
|
||||||
&& time <= endOfAlarm
|
&& time <= endOfAlarm
|
||||||
|
|
||||||
let scaledForAlarm light brightness =
|
let scaledForAlarm light brightness =
|
||||||
if light.ControlledWithRemote <> NonRemote && alarm then
|
if (lightProps light).ControlledWithRemote <> NonRemote && alarm then
|
||||||
float brightness * ((time - startOfDay) / (endOfAlarm - startOfDay)) |> byte
|
float brightness * ((time - startOfDay) / (endOfAlarm - startOfDay)) |> byte
|
||||||
else
|
else
|
||||||
brightness
|
brightness
|
||||||
|
|
@ -60,14 +60,14 @@ type NightLightTests() =
|
||||||
fakeHome.LightStates
|
fakeHome.LightStates
|
||||||
|> Seq.forall (fun (light, state) ->
|
|> Seq.forall (fun (light, state) ->
|
||||||
let maybeExpectedBrightness =
|
let maybeExpectedBrightness =
|
||||||
match light, state with
|
match (lightProps light).Bulb, state with
|
||||||
| _, Off -> None
|
| _, Off -> None
|
||||||
| { Bulb = IkeaBulb }, On(_, White) -> Some 254uy
|
| IkeaBulb, On(_, White) -> Some 254uy
|
||||||
| { Bulb = IkeaBulb }, On(_, Yellow) -> Some 210uy
|
| IkeaBulb, On(_, Yellow) -> Some 210uy
|
||||||
| { Bulb = IkeaBulb }, On(_, Red) -> Some 254uy
|
| IkeaBulb, On(_, Red) -> Some 254uy
|
||||||
| { Bulb = PaulmannBulb }, On(_, White) -> Some 35uy
|
| PaulmannBulb, On(_, White) -> Some 35uy
|
||||||
| { Bulb = PaulmannBulb }, On(_, Yellow) -> Some 35uy
|
| PaulmannBulb, On(_, Yellow) -> Some 35uy
|
||||||
| { Bulb = PaulmannBulb }, On(_, Red) -> Some 80uy
|
| PaulmannBulb, On(_, Red) -> Some 80uy
|
||||||
|> Option.map (scaledForAlarm light)
|
|> Option.map (scaledForAlarm light)
|
||||||
|
|
||||||
let maybeActualBrightness =
|
let maybeActualBrightness =
|
||||||
|
|
@ -109,7 +109,7 @@ type NightLightTests() =
|
||||||
let allOff (ls: (Light * LightState) seq) = ls |> Seq.forall (snd >> _.IsOff)
|
let allOff (ls: (Light * LightState) seq) = ls |> Seq.forall (snd >> _.IsOff)
|
||||||
|
|
||||||
let controlledBy remote ls =
|
let controlledBy remote ls =
|
||||||
ls |> Seq.filter (fst >> _.ControlledWithRemote >> (=) remote)
|
ls |> Seq.filter (fst >> lightProps >> _.ControlledWithRemote >> (=) remote)
|
||||||
|
|
||||||
let maybeLastRemoteInteraction = tryGetLastRemoteInteraction interactions
|
let maybeLastRemoteInteraction = tryGetLastRemoteInteraction interactions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,32 +46,52 @@ type LightControl =
|
||||||
| RemoteRight
|
| RemoteRight
|
||||||
|
|
||||||
type Light =
|
type Light =
|
||||||
|
| VardagsrumFonsterlampa
|
||||||
|
| VardagsrumVagglampa
|
||||||
|
| VardagsrumGolvlampa
|
||||||
|
| BadrumTaklampa
|
||||||
|
| SovrumNattduksbordlampa
|
||||||
|
|
||||||
|
type LightProps =
|
||||||
{ FriendlyName: DeviceFriendlyName
|
{ FriendlyName: DeviceFriendlyName
|
||||||
Room: Room
|
Room: Room
|
||||||
Bulb: Bulb
|
Bulb: Bulb
|
||||||
ControlledWithRemote: LightControl }
|
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 =
|
let lights =
|
||||||
[ { FriendlyName = DeviceFriendlyName "Vardagsrum - Fönsterlampa"
|
[ VardagsrumFonsterlampa
|
||||||
Room = Bedroom
|
VardagsrumVagglampa
|
||||||
Bulb = IkeaBulb
|
VardagsrumGolvlampa
|
||||||
ControlledWithRemote = RemoteRight }
|
BadrumTaklampa
|
||||||
{ FriendlyName = DeviceFriendlyName "Vardagsrum - Vägglampa"
|
SovrumNattduksbordlampa ]
|
||||||
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 } ]
|
|
||||||
|
|
||||||
let remoteControlFriendlyName = DeviceFriendlyName "Fjärrkontroll"
|
let remoteControlFriendlyName = DeviceFriendlyName "Fjärrkontroll"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@ open NightLight.Core.Moods
|
||||||
open FsToolkit.ErrorHandling
|
open FsToolkit.ErrorHandling
|
||||||
|
|
||||||
let internal tryFindLight friendlyName =
|
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) =
|
let internal generateZigbeeCommandsToFixLight (light: Light) (desiredLightState: LightState) =
|
||||||
seq {
|
seq {
|
||||||
match light.ControlledWithRemote, desiredLightState.State with
|
match (lightProps light).ControlledWithRemote, desiredLightState.State with
|
||||||
| NonRemote, On -> ()
|
| NonRemote, On -> ()
|
||||||
| NonRemote, Off -> failwith $"Unexpectly trying to turn off {light}. It's not remote-controlled."
|
| 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
|
| _, _ -> yield generateStateCommand desiredLightState.State light
|
||||||
|
|
||||||
if desiredLightState.State = On then
|
if desiredLightState.State = On then
|
||||||
|
|
@ -40,7 +40,8 @@ let internal createOrUpdateNightLightState
|
||||||
lights
|
lights
|
||||||
|> Seq.map (fun light ->
|
|> Seq.map (fun light ->
|
||||||
let color, brightness =
|
let color, brightness =
|
||||||
getDesiredMood light.Room partOfDay |> getDesiredColorAndBrightness light.Bulb
|
getDesiredMood (lightProps light).Room partOfDay
|
||||||
|
|> getDesiredColorAndBrightness (lightProps light).Bulb
|
||||||
|
|
||||||
let previousState =
|
let previousState =
|
||||||
maybeOldLightToState
|
maybeOldLightToState
|
||||||
|
|
@ -50,7 +51,7 @@ let internal createOrUpdateNightLightState
|
||||||
light,
|
light,
|
||||||
{ Color = color
|
{ Color = color
|
||||||
Brightness =
|
Brightness =
|
||||||
if alarm && light.ControlledWithRemote <> NonRemote then
|
if alarm && (lightProps light).ControlledWithRemote <> NonRemote then
|
||||||
brightness.Scale(getAlarmWeight time)
|
brightness.Scale(getAlarmWeight time)
|
||||||
else
|
else
|
||||||
brightness
|
brightness
|
||||||
|
|
@ -72,7 +73,7 @@ let internal withStateFor (light: Light) (state: State) (oldNightLightState: Nig
|
||||||
|
|
||||||
let internal withStateForRemoteControlledLights (state: State) (oldNightLightState: NightLightState) =
|
let internal withStateForRemoteControlledLights (state: State) (oldNightLightState: NightLightState) =
|
||||||
lights
|
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
|
|> Seq.fold (fun acc light -> acc |> withStateFor light state) oldNightLightState
|
||||||
|
|
||||||
let internal withAlarmOff (oldNightLightState: NightLightState) =
|
let internal withAlarmOff (oldNightLightState: NightLightState) =
|
||||||
|
|
@ -113,7 +114,8 @@ type NightLightStateMachine private (maybeState: NightLightState option) =
|
||||||
| PressedOff -> currentState |> withAlarmOff |> withStateForRemoteControlledLights Off
|
| PressedOff -> currentState |> withAlarmOff |> withStateForRemoteControlledLights Off
|
||||||
| PressedLeft ->
|
| PressedLeft ->
|
||||||
let lightThatShouldBeOn =
|
let lightThatShouldBeOn =
|
||||||
lights |> Seq.find (fun light -> light.ControlledWithRemote = RemoteLeft)
|
lights
|
||||||
|
|> Seq.find (fun light -> (lightProps light).ControlledWithRemote = RemoteLeft)
|
||||||
|
|
||||||
currentState
|
currentState
|
||||||
|> withAlarmOff
|
|> withAlarmOff
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ open System.Text.Json.Nodes
|
||||||
open NightLight.Core.Models
|
open NightLight.Core.Models
|
||||||
|
|
||||||
let toZigbeeCommand light payload =
|
let toZigbeeCommand light payload =
|
||||||
let topic = $"zigbee2mqtt/{light.FriendlyName.Get}/set"
|
let topic = $"zigbee2mqtt/{(lightProps light).FriendlyName.Get}/set"
|
||||||
{ Topic = topic; Payload = payload }
|
{ Topic = topic; Payload = payload }
|
||||||
|
|
||||||
let generateStateCommand state light =
|
let generateStateCommand state light =
|
||||||
|
|
@ -15,7 +15,7 @@ let generateStateCommand state light =
|
||||||
| On -> "ON"
|
| On -> "ON"
|
||||||
| Off -> "OFF"
|
| Off -> "OFF"
|
||||||
|
|
||||||
if light.Bulb = IkeaBulb then
|
if (lightProps light).Bulb = IkeaBulb then
|
||||||
commandObj["transition"] <- 0
|
commandObj["transition"] <- 0
|
||||||
|
|
||||||
commandObj.ToJsonString() |> toZigbeeCommand light
|
commandObj.ToJsonString() |> toZigbeeCommand light
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue