Introduce a State object instead of passing in partOfDay

We'll have some more complicated use cases for this later.
This commit is contained in:
Sven van Heugten 2026-01-03 18:58:00 +01:00
parent eebd72f40c
commit e226d092d8
2 changed files with 46 additions and 14 deletions

View file

@ -19,15 +19,20 @@ let internal generateZigbeeCommandToFixLight partOfDay light =
type Event = type Event =
| ReceivedZigbeeEvent of payload: string | 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 { result {
let partOfDay = state.PartOfDay
match event with match event with
| ReceivedZigbeeEvent payload -> | ReceivedZigbeeEvent payload ->
let! zigbeeEvent = parseZigbeeEvent payload let! zigbeeEvent = parseZigbeeEvent payload
return return
state,
match zigbeeEvent with match zigbeeEvent with
| DeviceAnnounce friendlyName -> | DeviceAnnounce friendlyName ->
let maybeLight = tryFindLight friendlyName let maybeLight = tryFindLight friendlyName
@ -35,5 +40,6 @@ let onEventReceived (partOfDay: PartOfDay) (event: Event) =
match maybeLight with match maybeLight with
| Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton | Some light -> generateZigbeeCommandToFixLight partOfDay light |> Seq.singleton
| None -> Seq.empty | None -> Seq.empty
| PartOfDayChanged -> return lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay) | PartOfDayChanged newPartOfDay ->
return { PartOfDay = newPartOfDay }, lights |> Seq.map (generateZigbeeCommandToFixLight partOfDay)
} }

View file

@ -1,5 +1,6 @@
open System open System
open System.Text open System.Text
open System.Threading
open System.Threading.Tasks open System.Threading.Tasks
open Microsoft.Extensions.Logging open Microsoft.Extensions.Logging
open MQTTnet open MQTTnet
@ -35,24 +36,32 @@ let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (c
|> Async.Ignore |> Async.Ignore
} }
let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (partOfDay: PartOfDay) (event: Event) = let private handleEvent (mqttClient: IMqttClient) (logger: ILogger) (state: State) (event: Event) =
let commandsResult = event |> onEventReceived partOfDay let result = event |> onEventReceived state
match commandsResult with match result with
| Ok commands -> publishZigbeeCommands mqttClient logger commands | Ok(newState, commands) ->
| Error UnknownType -> async.Return() async {
do! publishZigbeeCommands mqttClient logger commands
return newState
}
| Error UnknownType -> async.Return state
| Error e -> | Error e ->
logger.LogError("Error {Error} while {Event}", e, event) 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 payload = message.Payload
let decodedPayload = Encoding.UTF8.GetString(&payload) let decodedPayload = Encoding.UTF8.GetString(&payload)
logger.LogInformation("Received message with payload {Payload}", decodedPayload) logger.LogInformation("Received message with payload {Payload}", decodedPayload)
ReceivedZigbeeEvent decodedPayload ReceivedZigbeeEvent decodedPayload |> handleEvent mqttClient logger state
|> handleEvent mqttClient logger (getPartOfDay DateTime.Now)
[<EntryPoint>] [<EntryPoint>]
let mainAsync _ = let mainAsync _ =
@ -76,8 +85,19 @@ let mainAsync _ =
let mqttClientOptions = MqttClientOptionsBuilder().WithTcpServer(server).Build() let mqttClientOptions = MqttClientOptionsBuilder().WithTcpServer(server).Build()
let stateLock = new SemaphoreSlim(1, 1)
let mutable state = { PartOfDay = getPartOfDay DateTime.Now }
mqttClient.add_ApplicationMessageReceivedAsync (fun e -> 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 |> Async.StartAsTask
:> Task) :> Task)
@ -94,7 +114,13 @@ let mainAsync _ =
let currentPartOfDay = getPartOfDay DateTime.Now let currentPartOfDay = getPartOfDay DateTime.Now
if previousPartOfDay <> Some currentPartOfDay then 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 previousPartOfDay <- Some currentPartOfDay