From 9b6420b8a89c6389159c4cf872207a2c3f3f901d Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Thu, 1 Jan 2026 18:51:34 +0100 Subject: [PATCH 01/18] Make the day start at 5.30am instead --- NightLight.Core/PartsOfDay.fs | 6 +++++- README.md | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NightLight.Core/PartsOfDay.fs b/NightLight.Core/PartsOfDay.fs index f7b47d2..f50c002 100644 --- a/NightLight.Core/PartsOfDay.fs +++ b/NightLight.Core/PartsOfDay.fs @@ -8,5 +8,9 @@ type PartOfDay = let getPartOfDay (dateTime: DateTime) = match dateTime with - | _ when dateTime.TimeOfDay >= TimeSpan.FromHours 4.75 && dateTime.TimeOfDay < TimeSpan.FromHours 20.5 -> Day + | _ when + dateTime.TimeOfDay >= TimeSpan.FromHours 5.5 + && dateTime.TimeOfDay < TimeSpan.FromHours 20.5 + -> + Day | _ -> Night diff --git a/README.md b/README.md index 1446cf8..b70f026 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This is an F# program that turns all the lights in our apartment * _red_ at 8.30pm in the evening, and -* _white_/_yellow_ (depending on the room) at 4.45am in the morning. +* _white_/_yellow_ (depending on the room) at 5.30am in the morning. `NightLight.Core` is the functional core, and `NightLight` is the imperative shell. From f2864ffc47137d35fe58fc38e311bf31bb18142d Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Thu, 1 Jan 2026 19:24:42 +0100 Subject: [PATCH 02/18] Add .editorconfig --- .editorconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..23e9b2b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf + +[*.{fs,fsi,fsx}] +indent_size = 4 +indent_style = space +trim_trailing_whitespace=true + From 118dc03fdcaab5fde3ac0916b77f61c6e4d0fa3e Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 18:20:47 +0100 Subject: [PATCH 03/18] Transform onZigbeeEventReceived to onEventReceived We'll add different events later. --- NightLight.Core/Core.fs | 22 +++++++++++++--------- NightLight/Program.fs | 3 ++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index 742b43d..e8a4396 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -17,18 +17,22 @@ let internal generateZigbeeCommandToFixLight partOfDay light = generateZigbeeCommand light.FriendlyName color brightness -let onZigbeeEventReceived (partOfDay: PartOfDay) (decodedPayload: string) = +type Event = ReceivedZigbeeEvent of payload: string + +let onEventReceived (partOfDay: PartOfDay) (event: Event) = result { - let! zigbeeEvent = parseZigbeeEvent decodedPayload + match event with + | ReceivedZigbeeEvent payload -> + let! zigbeeEvent = parseZigbeeEvent payload - return - match zigbeeEvent with - | DeviceAnnounce friendlyName -> - let maybeLight = tryFindLight friendlyName + return + match zigbeeEvent with + | DeviceAnnounce friendlyName -> + let maybeLight = tryFindLight friendlyName - match maybeLight with - | Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton - | None -> Seq.empty + match maybeLight with + | Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton + | None -> Seq.empty } let onPartOfDayChanged (partOfDay: PartOfDay) = diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 9d390d2..cc22b9d 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -42,7 +42,8 @@ let private onMqttMessageReceived (mqttClient: IMqttClient) (logger: ILogger) (m logger.LogInformation("Received message with payload {Payload}", decodedPayload) let commandsResult = - decodedPayload |> onZigbeeEventReceived (getPartOfDay DateTime.Now) + ReceivedZigbeeEvent decodedPayload + |> onEventReceived (getPartOfDay DateTime.Now) match commandsResult with | Ok commands -> publishZigbeeCommands mqttClient logger commands From 7ccc523c43ff87617dc4fb034df2d728b8bff5e7 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 18:33:29 +0100 Subject: [PATCH 04/18] Introduce the handleEvent function in Program We'll add a second reference to it later. --- NightLight/Program.fs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/NightLight/Program.fs b/NightLight/Program.fs index cc22b9d..c8f3c85 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -35,22 +35,24 @@ let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (c |> Async.Ignore } +let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (partOfDay: PartOfDay) (event: Event) = + let commandsResult = event |> onEventReceived partOfDay + + match commandsResult with + | Ok commands -> publishZigbeeCommands mqttClient logger commands + | Error UnknownType -> async.Return() + | Error e -> + logger.LogError("Error {Error} while {Event}", e, event) + async.Return() + let private onMqttMessageReceived (mqttClient: IMqttClient) (logger: ILogger) (message: MqttApplicationMessage) = let payload = message.Payload let decodedPayload = Encoding.UTF8.GetString(&payload) logger.LogInformation("Received message with payload {Payload}", decodedPayload) - let commandsResult = - ReceivedZigbeeEvent decodedPayload - |> onEventReceived (getPartOfDay DateTime.Now) - - match commandsResult with - | Ok commands -> publishZigbeeCommands mqttClient logger commands - | Error UnknownType -> async.Return() - | Error e -> - logger.LogError("Error {Error} while processing {Payload}", e, payload) - async.Return() + ReceivedZigbeeEvent decodedPayload + |> handleEvent mqttClient logger (getPartOfDay DateTime.Now) [] let mainAsync _ = From eebd72f40c909c1408fe42fa8defe9dcdc43f0ee Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 18:34:08 +0100 Subject: [PATCH 05/18] Turn PartOfDayChanged into an event --- NightLight.Core/Core.fs | 8 ++++---- NightLight/Program.fs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index e8a4396..b78a026 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -17,7 +17,9 @@ let internal generateZigbeeCommandToFixLight partOfDay light = generateZigbeeCommand light.FriendlyName color brightness -type Event = ReceivedZigbeeEvent of payload: string +type Event = + | ReceivedZigbeeEvent of payload: string + | PartOfDayChanged let onEventReceived (partOfDay: PartOfDay) (event: Event) = result { @@ -33,7 +35,5 @@ let onEventReceived (partOfDay: PartOfDay) (event: Event) = match maybeLight with | Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton | None -> Seq.empty + | PartOfDayChanged -> return lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) } - -let onPartOfDayChanged (partOfDay: PartOfDay) = - lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) diff --git a/NightLight/Program.fs b/NightLight/Program.fs index c8f3c85..783678f 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -94,7 +94,8 @@ let mainAsync _ = let currentPartOfDay = getPartOfDay DateTime.Now if previousPartOfDay <> Some currentPartOfDay then - do! onPartOfDayChanged currentPartOfDay |> publishZigbeeCommands mqttClient logger + do! PartOfDayChanged |> handleEvent mqttClient logger currentPartOfDay + previousPartOfDay <- Some currentPartOfDay do! Async.Sleep 10_000 From e226d092d8b6cd12e6615b7cbdfab67465305e20 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 18:58:00 +0100 Subject: [PATCH 06/18] Introduce a State object instead of passing in partOfDay We'll have some more complicated use cases for this later. --- NightLight.Core/Core.fs | 12 ++++++++--- NightLight/Program.fs | 48 +++++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index b78a026..9d233f2 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -19,15 +19,20 @@ let internal generateZigbeeCommandToFixLight partOfDay light = type Event = | ReceivedZigbeeEvent of payload: string - | PartOfDayChanged + | PartOfDayChanged of newPartOfDay: PartOfDay -let onEventReceived (partOfDay: PartOfDay) (event: Event) = +type State = { PartOfDay: PartOfDay } + +let onEventReceived (state: State) (event: Event) = result { + let partOfDay = state.PartOfDay + match event with | ReceivedZigbeeEvent payload -> let! zigbeeEvent = parseZigbeeEvent payload return + state, match zigbeeEvent with | DeviceAnnounce friendlyName -> let maybeLight = tryFindLight friendlyName @@ -35,5 +40,6 @@ let onEventReceived (partOfDay: PartOfDay) (event: Event) = match maybeLight with | Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton | None -> Seq.empty - | PartOfDayChanged -> return lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) + | PartOfDayChanged newPartOfDay -> + return { PartOfDay = newPartOfDay }, lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) } diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 783678f..6c9121b 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -1,5 +1,6 @@ open System open System.Text +open System.Threading open System.Threading.Tasks open Microsoft.Extensions.Logging open MQTTnet @@ -35,24 +36,32 @@ let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (c |> Async.Ignore } -let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (partOfDay: PartOfDay) (event: Event) = - let commandsResult = event |> onEventReceived partOfDay +let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (state: State) (event: Event) = + let result = event |> onEventReceived state - match commandsResult with - | Ok commands -> publishZigbeeCommands mqttClient logger commands - | Error UnknownType -> async.Return() + match result with + | Ok(newState, commands) -> + async { + do! publishZigbeeCommands mqttClient logger commands + return newState + } + | Error UnknownType -> async.Return state | Error e -> logger.LogError("Error {Error} while {Event}", e, event) - async.Return() + async.Return state -let private onMqttMessageReceived (mqttClient: IMqttClient) (logger: ILogger) (message: MqttApplicationMessage) = +let private onMqttMessageReceived + (mqttClient: IMqttClient) + (logger: ILogger) + (state: State) + (message: MqttApplicationMessage) + = let payload = message.Payload let decodedPayload = Encoding.UTF8.GetString(&payload) logger.LogInformation("Received message with payload {Payload}", decodedPayload) - ReceivedZigbeeEvent decodedPayload - |> handleEvent mqttClient logger (getPartOfDay DateTime.Now) + ReceivedZigbeeEvent decodedPayload |> handleEvent mqttClient logger state [] let mainAsync _ = @@ -76,8 +85,19 @@ let mainAsync _ = let mqttClientOptions = MqttClientOptionsBuilder().WithTcpServer(server).Build() + let stateLock = new SemaphoreSlim(1, 1) + let mutable state = { PartOfDay = getPartOfDay DateTime.Now } + mqttClient.add_ApplicationMessageReceivedAsync (fun e -> - onMqttMessageReceived mqttClient logger e.ApplicationMessage + async { + do! stateLock.WaitAsync() |> Async.AwaitTask + + try + let! newState = onMqttMessageReceived mqttClient logger state e.ApplicationMessage + state <- newState + finally + stateLock.Release() |> ignore + } |> Async.StartAsTask :> Task) @@ -94,7 +114,13 @@ let mainAsync _ = let currentPartOfDay = getPartOfDay DateTime.Now if previousPartOfDay <> Some currentPartOfDay then - do! PartOfDayChanged |> handleEvent mqttClient logger currentPartOfDay + do! stateLock.WaitAsync() |> Async.AwaitTask + + try + let! newState = PartOfDayChanged currentPartOfDay |> handleEvent mqttClient logger state + state <- newState + finally + stateLock.Release() |> ignore previousPartOfDay <- Some currentPartOfDay From 506cf0c848933b97ee88d7d74e3fda0a6fce25cd Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:03:17 +0100 Subject: [PATCH 07/18] Accept TimeChanged events instead of PartOfDayChanged events --- NightLight.Core/Core.fs | 19 ++++++++++++++----- NightLight/Program.fs | 22 +++++++--------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index 9d233f2..888bc35 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -1,5 +1,6 @@ module NightLight.Core +open System open NightLight.PartsOfDay open NightLight.ZigbeeEvents open NightLight.ZigbeeCommands @@ -19,13 +20,13 @@ let internal generateZigbeeCommandToFixLight partOfDay light = type Event = | ReceivedZigbeeEvent of payload: string - | PartOfDayChanged of newPartOfDay: PartOfDay + | TimeChanged of DateTime -type State = { PartOfDay: PartOfDay } +type State = { Time: DateTime } let onEventReceived (state: State) (event: Event) = result { - let partOfDay = state.PartOfDay + let partOfDay = getPartOfDay state.Time match event with | ReceivedZigbeeEvent payload -> @@ -40,6 +41,14 @@ let onEventReceived (state: State) (event: Event) = match maybeLight with | Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton | None -> Seq.empty - | PartOfDayChanged newPartOfDay -> - return { PartOfDay = newPartOfDay }, lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) + | TimeChanged time -> + let newState = { Time = time } + let newPartOfDay = getPartOfDay time + + return + newState, + if partOfDay <> newPartOfDay then + lights |> Seq.map (generateZigbeeCommandToFixLight newPartOfDay) + else + Seq.empty } diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 6c9121b..aa7128a 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -5,7 +5,6 @@ open System.Threading.Tasks open Microsoft.Extensions.Logging open MQTTnet open MQTTnet.Protocol -open NightLight.PartsOfDay open NightLight.ZigbeeEvents open NightLight.ZigbeeCommands open NightLight.Core @@ -86,7 +85,7 @@ let mainAsync _ = let mqttClientOptions = MqttClientOptionsBuilder().WithTcpServer(server).Build() let stateLock = new SemaphoreSlim(1, 1) - let mutable state = { PartOfDay = getPartOfDay DateTime.Now } + let mutable state = { Time = DateTime.Now } mqttClient.add_ApplicationMessageReceivedAsync (fun e -> async { @@ -108,21 +107,14 @@ let mainAsync _ = |> Async.AwaitTask |> Async.Ignore - let mutable previousPartOfDay: PartOfDay option = None - while true do - let currentPartOfDay = getPartOfDay DateTime.Now + do! stateLock.WaitAsync() |> Async.AwaitTask - if previousPartOfDay <> Some currentPartOfDay then - do! stateLock.WaitAsync() |> Async.AwaitTask - - try - let! newState = PartOfDayChanged currentPartOfDay |> handleEvent mqttClient logger state - state <- newState - finally - stateLock.Release() |> ignore - - previousPartOfDay <- Some currentPartOfDay + try + let! newState = TimeChanged DateTime.Now |> handleEvent mqttClient logger state + state <- newState + finally + stateLock.Release() |> ignore do! Async.Sleep 10_000 From 31e9312731ace9aa87ad702fee3d6704d56854e8 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:04:48 +0100 Subject: [PATCH 08/18] Make the PartsOfDay module internal --- NightLight.Core/PartsOfDay.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NightLight.Core/PartsOfDay.fs b/NightLight.Core/PartsOfDay.fs index f50c002..b98aa7a 100644 --- a/NightLight.Core/PartsOfDay.fs +++ b/NightLight.Core/PartsOfDay.fs @@ -1,4 +1,4 @@ -module NightLight.PartsOfDay +module internal NightLight.PartsOfDay open System From ae66114ca15d395982693ccfba1f127dbdf501dc Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:06:58 +0100 Subject: [PATCH 09/18] Make the onEventReceived type more explicit --- NightLight.Core/Core.fs | 6 ++++-- NightLight/Program.fs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index 888bc35..d6e972a 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -22,15 +22,17 @@ type Event = | ReceivedZigbeeEvent of payload: string | TimeChanged of DateTime +type ParseEventError = ParseZigbeeEventError of ParseZigbeeEventError + type State = { Time: DateTime } -let onEventReceived (state: State) (event: Event) = +let onEventReceived (state: State) (event: Event) : Result = result { let partOfDay = getPartOfDay state.Time match event with | ReceivedZigbeeEvent payload -> - let! zigbeeEvent = parseZigbeeEvent payload + let! zigbeeEvent = parseZigbeeEvent payload |> Result.mapError ParseZigbeeEventError return state, diff --git a/NightLight/Program.fs b/NightLight/Program.fs index aa7128a..aa4a49a 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -44,7 +44,7 @@ let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (state: Stat do! publishZigbeeCommands mqttClient logger commands return newState } - | Error UnknownType -> async.Return state + | Error(ParseZigbeeEventError UnknownType) -> async.Return state | Error e -> logger.LogError("Error {Error} while {Event}", e, event) async.Return state From 915d01e086244c6d98100284614e6ff1d70787cc Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:14:09 +0100 Subject: [PATCH 10/18] Introduce mqttMessageToReceivedZigbeeEvent To make it more obvious that both cases in mainAsync just handle events. --- NightLight/Program.fs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/NightLight/Program.fs b/NightLight/Program.fs index aa4a49a..16de5e0 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -36,6 +36,10 @@ let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (c } let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (state: State) (event: Event) = + match event with + | ReceivedZigbeeEvent payload -> logger.LogInformation("Received message with payload {Payload}", payload) + | _ -> () + let result = event |> onEventReceived state match result with @@ -49,18 +53,11 @@ let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (state: Stat logger.LogError("Error {Error} while {Event}", e, event) async.Return state -let private onMqttMessageReceived - (mqttClient: IMqttClient) - (logger: ILogger) - (state: State) - (message: MqttApplicationMessage) - = +let private mqttMessageToReceivedZigbeeEvent (message: MqttApplicationMessage) = let payload = message.Payload let decodedPayload = Encoding.UTF8.GetString(&payload) - logger.LogInformation("Received message with payload {Payload}", decodedPayload) - - ReceivedZigbeeEvent decodedPayload |> handleEvent mqttClient logger state + ReceivedZigbeeEvent decodedPayload [] let mainAsync _ = @@ -89,10 +86,12 @@ let mainAsync _ = mqttClient.add_ApplicationMessageReceivedAsync (fun e -> async { + let event = mqttMessageToReceivedZigbeeEvent e.ApplicationMessage + do! stateLock.WaitAsync() |> Async.AwaitTask try - let! newState = onMqttMessageReceived mqttClient logger state e.ApplicationMessage + let! newState = event |> handleEvent mqttClient logger state state <- newState finally stateLock.Release() |> ignore From c377ec25dccee044a39a6dff61cc9034f86ad6cf Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:24:37 +0100 Subject: [PATCH 11/18] Introduce the Models module in order to make others internal --- NightLight.Core/Core.fs | 10 +--------- NightLight.Core/Models.fs | 22 ++++++++++++++++++++++ NightLight.Core/NightLight.Core.fsproj | 1 + NightLight.Core/ZigbeeCommands.fs | 7 +++---- NightLight.Core/ZigbeeEvents.fs | 14 +++----------- NightLight/Program.fs | 3 +-- 6 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 NightLight.Core/Models.fs diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index d6e972a..eb0c4f8 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -1,6 +1,6 @@ module NightLight.Core -open System +open NightLight.Models open NightLight.PartsOfDay open NightLight.ZigbeeEvents open NightLight.ZigbeeCommands @@ -18,14 +18,6 @@ let internal generateZigbeeCommandToFixLight partOfDay light = generateZigbeeCommand light.FriendlyName color brightness -type Event = - | ReceivedZigbeeEvent of payload: string - | TimeChanged of DateTime - -type ParseEventError = ParseZigbeeEventError of ParseZigbeeEventError - -type State = { Time: DateTime } - let onEventReceived (state: State) (event: Event) : Result = result { let partOfDay = getPartOfDay state.Time diff --git a/NightLight.Core/Models.fs b/NightLight.Core/Models.fs new file mode 100644 index 0000000..f4e6dc4 --- /dev/null +++ b/NightLight.Core/Models.fs @@ -0,0 +1,22 @@ +module NightLight.Models + +open System + +type State = { Time: DateTime } + +type Event = + | ReceivedZigbeeEvent of payload: string + | TimeChanged of DateTime + +type ZigbeeCommand = ZigbeeCommand of Topic: string * Payload: string + +type ParseZigbeeEventError = + | InvalidJson + | MissingTypeField + | MissingDataField + | MissingFriendlyNameField + | InvalidTypeField + | InvalidFriendlyNameField + | UnknownType + +type ParseEventError = ParseZigbeeEventError of ParseZigbeeEventError diff --git a/NightLight.Core/NightLight.Core.fsproj b/NightLight.Core/NightLight.Core.fsproj index d8796ce..aea404b 100644 --- a/NightLight.Core/NightLight.Core.fsproj +++ b/NightLight.Core/NightLight.Core.fsproj @@ -6,6 +6,7 @@ + diff --git a/NightLight.Core/ZigbeeCommands.fs b/NightLight.Core/ZigbeeCommands.fs index 2ebbb08..f1572f1 100644 --- a/NightLight.Core/ZigbeeCommands.fs +++ b/NightLight.Core/ZigbeeCommands.fs @@ -1,11 +1,10 @@ -module NightLight.ZigbeeCommands +module internal NightLight.ZigbeeCommands open System.Text.Json.Nodes +open NightLight.Models open NightLight.Lights -type ZigbeeCommand = ZigbeeCommand of Topic: string * Payload: string - -let internal generateZigbeeCommand friendlyName targetColor targetBrightness = +let generateZigbeeCommand friendlyName targetColor targetBrightness = let commandObj = JsonObject() match targetColor with diff --git a/NightLight.Core/ZigbeeEvents.fs b/NightLight.Core/ZigbeeEvents.fs index 24f0b02..6dc6205 100644 --- a/NightLight.Core/ZigbeeEvents.fs +++ b/NightLight.Core/ZigbeeEvents.fs @@ -1,20 +1,12 @@ -module NightLight.ZigbeeEvents +module internal NightLight.ZigbeeEvents +open NightLight.Models open FsToolkit.ErrorHandling open FSharp.Data type ZigbeeEvent = DeviceAnnounce of FriendlyName: string -type ParseZigbeeEventError = - | InvalidJson - | MissingTypeField - | MissingDataField - | MissingFriendlyNameField - | InvalidTypeField - | InvalidFriendlyNameField - | UnknownType - -let internal parseZigbeeEvent str = +let parseZigbeeEvent str = result { let! jsonValue = JsonValue.TryParse str |> Result.requireSome InvalidJson diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 16de5e0..7aa70ff 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -5,8 +5,7 @@ open System.Threading.Tasks open Microsoft.Extensions.Logging open MQTTnet open MQTTnet.Protocol -open NightLight.ZigbeeEvents -open NightLight.ZigbeeCommands +open NightLight.Models open NightLight.Core let private generateMqttMessage zigbeeCommand = From 12d0ecf0e6b9ddd19183518b9ca639ffb79ce619 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Thu, 1 Jan 2026 19:36:13 +0100 Subject: [PATCH 12/18] Be a bit more diligent with task creation --- NightLight/Program.fs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 7aa70ff..6415415 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -28,8 +28,7 @@ let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (c return! commands |> Seq.map generateMqttMessage - |> Seq.map mqttClient.PublishAsync - |> Seq.map Async.AwaitTask + |> Seq.map (fun message -> async { return! mqttClient.PublishAsync message |> Async.AwaitTask }) |> Async.Sequential |> Async.Ignore } From ab6e123efc309bd9ee34dbb9c8e771a83e866e74 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:30:43 +0100 Subject: [PATCH 13/18] ZigbeeCommand -> Message There isn't really anything specifically Zigbee about this type. --- NightLight.Core/Models.fs | 2 +- NightLight.Core/ZigbeeCommands.fs | 2 +- NightLight/Program.fs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/NightLight.Core/Models.fs b/NightLight.Core/Models.fs index f4e6dc4..161902f 100644 --- a/NightLight.Core/Models.fs +++ b/NightLight.Core/Models.fs @@ -8,7 +8,7 @@ type Event = | ReceivedZigbeeEvent of payload: string | TimeChanged of DateTime -type ZigbeeCommand = ZigbeeCommand of Topic: string * Payload: string +type Message = Message of Topic: string * Payload: string type ParseZigbeeEventError = | InvalidJson diff --git a/NightLight.Core/ZigbeeCommands.fs b/NightLight.Core/ZigbeeCommands.fs index f1572f1..946b7c4 100644 --- a/NightLight.Core/ZigbeeCommands.fs +++ b/NightLight.Core/ZigbeeCommands.fs @@ -22,4 +22,4 @@ let generateZigbeeCommand friendlyName targetColor targetBrightness = let topic = $"zigbee2mqtt/{friendlyName}/set" let payload = commandObj.ToJsonString() - ZigbeeCommand(topic, payload) + Message(topic, payload) diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 6415415..a1a4326 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -10,7 +10,7 @@ open NightLight.Core let private generateMqttMessage zigbeeCommand = match zigbeeCommand with - | ZigbeeCommand(topic, payload) -> + | Message(topic, payload) -> MqttApplicationMessageBuilder() .WithTopic(topic) .WithPayload(payload) @@ -22,7 +22,7 @@ let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (c commands |> Seq.iter (fun command -> match command with - | ZigbeeCommand(topic, payload) -> + | Message(topic, payload) -> logger.LogInformation("Publishing message {Payload} to topic {Topic}...", payload, topic)) return! From 10e26a48664608aed71416589d2d3fe477d46265 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:34:33 +0100 Subject: [PATCH 14/18] Turn Message into a record type --- NightLight.Core/Core.fs | 2 +- NightLight.Core/Models.fs | 2 +- NightLight.Core/ZigbeeCommands.fs | 2 +- NightLight/Program.fs | 18 +++++++----------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index eb0c4f8..4a5ed2f 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -18,7 +18,7 @@ let internal generateZigbeeCommandToFixLight partOfDay light = generateZigbeeCommand light.FriendlyName color brightness -let onEventReceived (state: State) (event: Event) : Result = +let onEventReceived (state: State) (event: Event) : Result = result { let partOfDay = getPartOfDay state.Time diff --git a/NightLight.Core/Models.fs b/NightLight.Core/Models.fs index 161902f..23bd86d 100644 --- a/NightLight.Core/Models.fs +++ b/NightLight.Core/Models.fs @@ -8,7 +8,7 @@ type Event = | ReceivedZigbeeEvent of payload: string | TimeChanged of DateTime -type Message = Message of Topic: string * Payload: string +type Message = { Topic: string; Payload: string } type ParseZigbeeEventError = | InvalidJson diff --git a/NightLight.Core/ZigbeeCommands.fs b/NightLight.Core/ZigbeeCommands.fs index 946b7c4..12cdb46 100644 --- a/NightLight.Core/ZigbeeCommands.fs +++ b/NightLight.Core/ZigbeeCommands.fs @@ -22,4 +22,4 @@ let generateZigbeeCommand friendlyName targetColor targetBrightness = let topic = $"zigbee2mqtt/{friendlyName}/set" let payload = commandObj.ToJsonString() - Message(topic, payload) + { Topic = topic; Payload = payload } diff --git a/NightLight/Program.fs b/NightLight/Program.fs index a1a4326..7daee6b 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -9,21 +9,17 @@ open NightLight.Models open NightLight.Core let private generateMqttMessage zigbeeCommand = - match zigbeeCommand with - | Message(topic, payload) -> - MqttApplicationMessageBuilder() - .WithTopic(topic) - .WithPayload(payload) - .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) - .Build() + MqttApplicationMessageBuilder() + .WithTopic(zigbeeCommand.Topic) + .WithPayload(zigbeeCommand.Payload) + .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce) + .Build() -let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (commands: ZigbeeCommand seq) = +let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (commands: Message seq) = async { commands |> Seq.iter (fun command -> - match command with - | Message(topic, payload) -> - logger.LogInformation("Publishing message {Payload} to topic {Topic}...", payload, topic)) + logger.LogInformation("Publishing message {Payload} to topic {Topic}...", command.Payload, command.Topic)) return! commands From 27c66462370dfdba94cf1676ffe8e2541b91fa56 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:36:25 +0100 Subject: [PATCH 15/18] Put a full Message in ReceivedZigbeeEvent We'll need the topic later. --- NightLight.Core/Models.fs | 8 ++++---- NightLight.Core/ZigbeeEvents.fs | 4 ++-- NightLight/Program.fs | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/NightLight.Core/Models.fs b/NightLight.Core/Models.fs index 23bd86d..f8068bf 100644 --- a/NightLight.Core/Models.fs +++ b/NightLight.Core/Models.fs @@ -4,12 +4,12 @@ open System type State = { Time: DateTime } -type Event = - | ReceivedZigbeeEvent of payload: string - | TimeChanged of DateTime - type Message = { Topic: string; Payload: string } +type Event = + | ReceivedZigbeeEvent of Message + | TimeChanged of DateTime + type ParseZigbeeEventError = | InvalidJson | MissingTypeField diff --git a/NightLight.Core/ZigbeeEvents.fs b/NightLight.Core/ZigbeeEvents.fs index 6dc6205..b0f9d76 100644 --- a/NightLight.Core/ZigbeeEvents.fs +++ b/NightLight.Core/ZigbeeEvents.fs @@ -6,9 +6,9 @@ open FSharp.Data type ZigbeeEvent = DeviceAnnounce of FriendlyName: string -let parseZigbeeEvent str = +let parseZigbeeEvent (message: Message) = result { - let! jsonValue = JsonValue.TryParse str |> Result.requireSome InvalidJson + let! jsonValue = JsonValue.TryParse message.Payload |> Result.requireSome InvalidJson let! messageType = jsonValue.TryGetProperty "type" |> Result.requireSome MissingTypeField let! messageData = jsonValue.TryGetProperty "data" |> Result.requireSome MissingDataField diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 7daee6b..59aed70 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -51,7 +51,9 @@ let private mqttMessageToReceivedZigbeeEvent (message: MqttApplicationMessage) = let payload = message.Payload let decodedPayload = Encoding.UTF8.GetString(&payload) - ReceivedZigbeeEvent decodedPayload + ReceivedZigbeeEvent + { Topic = message.Topic + Payload = decodedPayload } [] let mainAsync _ = From 7ea6e49001e287169ce597c3f6a59edd9655850f Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:37:31 +0100 Subject: [PATCH 16/18] Introduce decodeMqttApplicationMessage --- NightLight/Program.fs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 59aed70..0c8d153 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -47,13 +47,12 @@ let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (state: Stat logger.LogError("Error {Error} while {Event}", e, event) async.Return state -let private mqttMessageToReceivedZigbeeEvent (message: MqttApplicationMessage) = +let private decodeMqttApplicationMessage (message: MqttApplicationMessage) = let payload = message.Payload let decodedPayload = Encoding.UTF8.GetString(&payload) - ReceivedZigbeeEvent - { Topic = message.Topic - Payload = decodedPayload } + { Topic = message.Topic + Payload = decodedPayload } [] let mainAsync _ = @@ -82,7 +81,7 @@ let mainAsync _ = mqttClient.add_ApplicationMessageReceivedAsync (fun e -> async { - let event = mqttMessageToReceivedZigbeeEvent e.ApplicationMessage + let event = ReceivedZigbeeEvent <| decodeMqttApplicationMessage e.ApplicationMessage do! stateLock.WaitAsync() |> Async.AwaitTask From 5219abfb4b42caf19327740c5df621feec4c42b6 Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 19:55:51 +0100 Subject: [PATCH 17/18] Fix incorrect namespaces --- NightLight.Core/Configuration.fs | 6 +++--- NightLight.Core/Core.fs | 16 ++++++++-------- NightLight.Core/Lights.fs | 4 ++-- NightLight.Core/Models.fs | 2 +- NightLight.Core/Moods.fs | 4 ++-- NightLight.Core/PartsOfDay.fs | 2 +- NightLight.Core/ZigbeeCommands.fs | 6 +++--- NightLight.Core/ZigbeeEvents.fs | 4 ++-- NightLight/Program.fs | 4 ++-- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/NightLight.Core/Configuration.fs b/NightLight.Core/Configuration.fs index 6f50b26..6658ec7 100644 --- a/NightLight.Core/Configuration.fs +++ b/NightLight.Core/Configuration.fs @@ -1,7 +1,7 @@ -module internal NightLight.Configuration +module internal NightLight.Core.Configuration -open NightLight.Moods -open NightLight.Lights +open NightLight.Core.Moods +open NightLight.Core.Lights let getDesiredColorAndBrightness bulb mood = let white = ColorByCoordinates(0.3227, 0.329) diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index 4a5ed2f..2b1cc3b 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -1,12 +1,12 @@ -module NightLight.Core +module NightLight.Core.Core -open NightLight.Models -open NightLight.PartsOfDay -open NightLight.ZigbeeEvents -open NightLight.ZigbeeCommands -open NightLight.Moods -open NightLight.Lights -open NightLight.Configuration +open NightLight.Core.Models +open NightLight.Core.PartsOfDay +open NightLight.Core.ZigbeeEvents +open NightLight.Core.ZigbeeCommands +open NightLight.Core.Moods +open NightLight.Core.Lights +open NightLight.Core.Configuration open FsToolkit.ErrorHandling let internal tryFindLight friendlyName = diff --git a/NightLight.Core/Lights.fs b/NightLight.Core/Lights.fs index be03dbf..c68d96d 100644 --- a/NightLight.Core/Lights.fs +++ b/NightLight.Core/Lights.fs @@ -1,6 +1,6 @@ -module internal NightLight.Lights +module internal NightLight.Core.Lights -open NightLight.Moods +open NightLight.Core.Moods type Bulb = | IkeaBulb diff --git a/NightLight.Core/Models.fs b/NightLight.Core/Models.fs index f8068bf..35736b0 100644 --- a/NightLight.Core/Models.fs +++ b/NightLight.Core/Models.fs @@ -1,4 +1,4 @@ -module NightLight.Models +module NightLight.Core.Models open System diff --git a/NightLight.Core/Moods.fs b/NightLight.Core/Moods.fs index c9c8d44..bf24695 100644 --- a/NightLight.Core/Moods.fs +++ b/NightLight.Core/Moods.fs @@ -1,6 +1,6 @@ -module internal NightLight.Moods +module internal NightLight.Core.Moods -open NightLight.PartsOfDay +open NightLight.Core.PartsOfDay type Mood = | White diff --git a/NightLight.Core/PartsOfDay.fs b/NightLight.Core/PartsOfDay.fs index b98aa7a..8cfc3c3 100644 --- a/NightLight.Core/PartsOfDay.fs +++ b/NightLight.Core/PartsOfDay.fs @@ -1,4 +1,4 @@ -module internal NightLight.PartsOfDay +module internal NightLight.Core.PartsOfDay open System diff --git a/NightLight.Core/ZigbeeCommands.fs b/NightLight.Core/ZigbeeCommands.fs index 12cdb46..ef8c4c8 100644 --- a/NightLight.Core/ZigbeeCommands.fs +++ b/NightLight.Core/ZigbeeCommands.fs @@ -1,8 +1,8 @@ -module internal NightLight.ZigbeeCommands +module internal NightLight.Core.ZigbeeCommands open System.Text.Json.Nodes -open NightLight.Models -open NightLight.Lights +open NightLight.Core.Models +open NightLight.Core.Lights let generateZigbeeCommand friendlyName targetColor targetBrightness = let commandObj = JsonObject() diff --git a/NightLight.Core/ZigbeeEvents.fs b/NightLight.Core/ZigbeeEvents.fs index b0f9d76..b435698 100644 --- a/NightLight.Core/ZigbeeEvents.fs +++ b/NightLight.Core/ZigbeeEvents.fs @@ -1,6 +1,6 @@ -module internal NightLight.ZigbeeEvents +module internal NightLight.Core.ZigbeeEvents -open NightLight.Models +open NightLight.Core.Models open FsToolkit.ErrorHandling open FSharp.Data diff --git a/NightLight/Program.fs b/NightLight/Program.fs index 0c8d153..c140173 100644 --- a/NightLight/Program.fs +++ b/NightLight/Program.fs @@ -5,8 +5,8 @@ open System.Threading.Tasks open Microsoft.Extensions.Logging open MQTTnet open MQTTnet.Protocol -open NightLight.Models -open NightLight.Core +open NightLight.Core.Models +open NightLight.Core.Core let private generateMqttMessage zigbeeCommand = MqttApplicationMessageBuilder() From 83b716a509e08f7bb7b1c16ab1507993f9466ace Mon Sep 17 00:00:00 2001 From: Sven van Heugten Date: Sat, 3 Jan 2026 20:03:18 +0100 Subject: [PATCH 18/18] Reorganize types to prepare for unit testing --- NightLight.Core/Configuration.fs | 34 -------------------------- NightLight.Core/Core.fs | 2 -- NightLight.Core/Lights.fs | 18 -------------- NightLight.Core/Models.fs | 31 +++++++++++++++++++++++ NightLight.Core/Moods.fs | 25 +++++++++++++++---- NightLight.Core/NightLight.Core.fsproj | 2 -- NightLight.Core/ZigbeeCommands.fs | 2 +- 7 files changed, 52 insertions(+), 62 deletions(-) delete mode 100644 NightLight.Core/Configuration.fs delete mode 100644 NightLight.Core/Lights.fs diff --git a/NightLight.Core/Configuration.fs b/NightLight.Core/Configuration.fs deleted file mode 100644 index 6658ec7..0000000 --- a/NightLight.Core/Configuration.fs +++ /dev/null @@ -1,34 +0,0 @@ -module internal NightLight.Core.Configuration - -open NightLight.Core.Moods -open NightLight.Core.Lights - -let getDesiredColorAndBrightness bulb mood = - let white = ColorByCoordinates(0.3227, 0.329) - let yellow = ColorByTemperature 454 - let red = ColorByCoordinates(0.6942, 0.2963) - - match bulb, mood with - | IkeaBulb, White -> white, Brightness 254 - | IkeaBulb, Yellow -> yellow, Brightness 210 - | IkeaBulb, Red -> red, Brightness 254 - | PaulmannBulb, White -> white, Brightness 35 - | PaulmannBulb, Yellow -> yellow, Brightness 35 - | PaulmannBulb, Red -> red, Brightness 80 - -let lights = - [ { FriendlyName = "Vardagsrum - Fönsterlampa" - Room = LivingRoom - Bulb = IkeaBulb } - { FriendlyName = "Vardagsrum - Vägglampa" - Room = LivingRoom - Bulb = PaulmannBulb } - { FriendlyName = "Vardagsrum - Golvlampa" - Room = LivingRoom - Bulb = PaulmannBulb } - { FriendlyName = "Badrum - Taklampa" - Room = Bathroom - Bulb = IkeaBulb } - { FriendlyName = "Sovrum - Nattduksbordlampa" - Room = Bedroom - Bulb = IkeaBulb } ] diff --git a/NightLight.Core/Core.fs b/NightLight.Core/Core.fs index 2b1cc3b..83cc290 100644 --- a/NightLight.Core/Core.fs +++ b/NightLight.Core/Core.fs @@ -5,8 +5,6 @@ open NightLight.Core.PartsOfDay open NightLight.Core.ZigbeeEvents open NightLight.Core.ZigbeeCommands open NightLight.Core.Moods -open NightLight.Core.Lights -open NightLight.Core.Configuration open FsToolkit.ErrorHandling let internal tryFindLight friendlyName = diff --git a/NightLight.Core/Lights.fs b/NightLight.Core/Lights.fs deleted file mode 100644 index c68d96d..0000000 --- a/NightLight.Core/Lights.fs +++ /dev/null @@ -1,18 +0,0 @@ -module internal NightLight.Core.Lights - -open NightLight.Core.Moods - -type Bulb = - | IkeaBulb - | PaulmannBulb - -type Color = - | ColorByCoordinates of float * float - | ColorByTemperature of int - -type Brightness = Brightness of int - -type Light = - { FriendlyName: string - Room: Room - Bulb: Bulb } diff --git a/NightLight.Core/Models.fs b/NightLight.Core/Models.fs index 35736b0..2fb7f54 100644 --- a/NightLight.Core/Models.fs +++ b/NightLight.Core/Models.fs @@ -20,3 +20,34 @@ type ParseZigbeeEventError = | UnknownType type ParseEventError = ParseZigbeeEventError of ParseZigbeeEventError + +type Room = + | Bathroom + | LivingRoom + | Bedroom + +type Bulb = + | IkeaBulb + | PaulmannBulb + +type Light = + { FriendlyName: string + Room: Room + Bulb: Bulb } + +let lights = + [ { FriendlyName = "Vardagsrum - Fönsterlampa" + Room = LivingRoom + Bulb = IkeaBulb } + { FriendlyName = "Vardagsrum - Vägglampa" + Room = LivingRoom + Bulb = PaulmannBulb } + { FriendlyName = "Vardagsrum - Golvlampa" + Room = LivingRoom + Bulb = PaulmannBulb } + { FriendlyName = "Badrum - Taklampa" + Room = Bathroom + Bulb = IkeaBulb } + { FriendlyName = "Sovrum - Nattduksbordlampa" + Room = Bedroom + Bulb = IkeaBulb } ] diff --git a/NightLight.Core/Moods.fs b/NightLight.Core/Moods.fs index bf24695..9f33cf3 100644 --- a/NightLight.Core/Moods.fs +++ b/NightLight.Core/Moods.fs @@ -1,20 +1,35 @@ module internal NightLight.Core.Moods open NightLight.Core.PartsOfDay +open NightLight.Core.Models type Mood = | White | Yellow | Red -type Room = - | Bathroom - | LivingRoom - | Bedroom - let getDesiredMood room partOfDay = match room, partOfDay with | Bathroom, Day -> White | LivingRoom, Day -> Yellow | Bedroom, Day -> Yellow | _, Night -> Red + +type Color = + | ColorByCoordinates of float * float + | ColorByTemperature of int + +type Brightness = Brightness of int + +let getDesiredColorAndBrightness bulb mood = + let white = ColorByCoordinates(0.3227, 0.329) + let yellow = ColorByTemperature 454 + let red = ColorByCoordinates(0.6942, 0.2963) + + match bulb, mood with + | IkeaBulb, White -> white, Brightness 254 + | IkeaBulb, Yellow -> yellow, Brightness 210 + | IkeaBulb, Red -> red, Brightness 254 + | PaulmannBulb, White -> white, Brightness 35 + | PaulmannBulb, Yellow -> yellow, Brightness 35 + | PaulmannBulb, Red -> red, Brightness 80 diff --git a/NightLight.Core/NightLight.Core.fsproj b/NightLight.Core/NightLight.Core.fsproj index aea404b..31b2b6e 100644 --- a/NightLight.Core/NightLight.Core.fsproj +++ b/NightLight.Core/NightLight.Core.fsproj @@ -9,10 +9,8 @@ - - diff --git a/NightLight.Core/ZigbeeCommands.fs b/NightLight.Core/ZigbeeCommands.fs index ef8c4c8..6df6e85 100644 --- a/NightLight.Core/ZigbeeCommands.fs +++ b/NightLight.Core/ZigbeeCommands.fs @@ -2,7 +2,7 @@ module internal NightLight.Core.ZigbeeCommands open System.Text.Json.Nodes open NightLight.Core.Models -open NightLight.Core.Lights +open NightLight.Core.Moods let generateZigbeeCommand friendlyName targetColor targetBrightness = let commandObj = JsonObject()