Initial commit

This commit is contained in:
Sven van Heugten 2025-11-14 20:40:45 +01:00
commit 284fdc1261
16 changed files with 448 additions and 0 deletions

13
NightLight/Dockerfile Normal file
View file

@ -0,0 +1,13 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /app
COPY NightLight.sln ./
COPY NightLight.Core/NightLight.Core.fsproj NightLight.Core/
COPY NightLight/NightLight.fsproj NightLight/
RUN dotnet restore
COPY . ./
RUN dotnet publish NightLight/NightLight.fsproj --no-restore -c Release -o out
FROM mcr.microsoft.com/dotnet/runtime:9.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "NightLight.dll"]

View file

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.4" />
<PackageReference Include="MQTTnet" Version="5.0.1.1416" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../NightLight.Core/NightLight.Core.fsproj" />
</ItemGroup>
</Project>

103
NightLight/Program.fs Normal file
View file

@ -0,0 +1,103 @@
open System
open System.Text
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
let private generateMqttMessage zigbeeCommand =
match zigbeeCommand with
| ZigbeeCommand(topic, payload) ->
MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build()
let private publishZigbeeCommands (mqttClient: IMqttClient) (logger: ILogger) (commands: ZigbeeCommand seq) =
async {
commands
|> Seq.iter (fun command ->
match command with
| ZigbeeCommand(topic, payload) ->
logger.LogInformation("Publishing message {Payload} to topic {Topic}...", payload, topic))
return!
commands
|> Seq.map generateMqttMessage
|> Seq.map mqttClient.PublishAsync
|> Seq.map Async.AwaitTask
|> Async.Sequential
|> Async.Ignore
}
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 =
decodedPayload |> onZigbeeEventReceived (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()
[<EntryPoint>]
let mainAsync _ =
// This is still a stateful mess. Needs to be cleaned up a lot.
async {
let loggerFactory =
LoggerFactory.Create(fun builder -> builder.AddConsole().SetMinimumLevel LogLevel.Information |> ignore)
let logger = loggerFactory.CreateLogger "NightLight"
logger.LogInformation("Current system time is {Now}", DateTime.Now)
let mqttFactory = MqttClientFactory()
use mqttClient = mqttFactory.CreateMqttClient()
let server =
match Environment.GetEnvironmentVariable "MQTT_SERVER" with
| null -> "localhost"
| value -> value
let mqttClientOptions = MqttClientOptionsBuilder().WithTcpServer(server).Build()
mqttClient.add_ApplicationMessageReceivedAsync (fun e ->
onMqttMessageReceived mqttClient logger e.ApplicationMessage
|> Async.StartAsTask
:> Task)
do! mqttClient.ConnectAsync mqttClientOptions |> Async.AwaitTask |> Async.Ignore
do!
mqttClient.SubscribeAsync "zigbee2mqtt/bridge/event"
|> Async.AwaitTask
|> Async.Ignore
let mutable previousPartOfDay: PartOfDay option = None
while true do
let currentPartOfDay = getPartOfDay DateTime.Now
if previousPartOfDay <> Some currentPartOfDay then
do! onPartOfDayChanged currentPartOfDay |> publishZigbeeCommands mqttClient logger
previousPartOfDay <- Some currentPartOfDay
do! Async.Sleep 10_000
do! mqttClient.DisconnectAsync() |> Async.AwaitTask
}
|> Async.RunSynchronously
0