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 =
| 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)
}

View file

@ -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
[<EntryPoint>]
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