From 260a8192534b559e1080265b67e43ac4d6b0c90e Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 16:20:07 +0200 Subject: [PATCH 01/23] Add first README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..26e6958 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# DisplayUtil + +DisplayUtil is a C# webserver for building status screens for an ESP32 with a +epaper display. \ No newline at end of file From a5be272657251743dfc89383d3bba1be7ffd6a2a Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 16:21:00 +0200 Subject: [PATCH 02/23] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdc2049 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Tim Ittermann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From e887e4ba145af7c21e7a5fe681970783f73a0757 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 16:46:59 +0200 Subject: [PATCH 03/23] Move to subfolder --- DisplayUtil.sln | 2 +- .../DisplayUtil.csproj | 56 +++++----- .../EspUtilities}/BinaryImageStreamCreater.cs | 0 .../EspUtilities}/EspImageProvider.cs | 0 .../EspUtilitiesInitExtension.cs | 0 .../EspUtilities}/RunLengthCompressor.cs | 0 .../Calendar/HassAppointmentStore.cs | 0 .../Calendar/HassCalendarJob.cs | 0 .../Calendar/HassCalendarSettings.cs | 0 .../Calendar/HassCalendarWorker.cs | 0 .../HomeAssistant}/HassExtensions.cs | 0 .../HomeAssistant}/HassHostedService.cs | 0 .../HomeAssistant}/HassTemplateExtender.cs | 0 .../Layouting}/DrawManager.cs | 0 .../Layouting}/Element.cs | 0 .../Layouting}/ElementCollection.cs | 0 .../Layouting}/FlexboxElement.cs | 0 .../Layouting}/HBoxElement.cs | 0 .../Layouting}/IconElement.cs | 0 .../Layouting}/SiteSize.cs | 0 .../Layouting}/TextElement.cs | 0 .../Layouting}/VBoxElement.cs | 0 .../MqttExport}/ExportingMqttClient.cs | 0 .../MqttExport}/MqttExportJob.cs | 0 .../MqttExport}/MqttExporter.cs | 0 .../MqttExport}/MqttInitExtension.cs | 0 .../MqttExport}/MqttSettings.cs | 0 .../MqttExport}/MqttUrlRenderer.cs | 0 Program.cs => DisplayUtil/Program.cs | 100 +++++++++--------- .../Properties}/launchSettings.json | 68 ++++++------ .../Providers}/FontProvider.cs | 0 .../Providers}/GFontDownloader.cs | 0 .../Providers}/IconPathProvider.cs | 0 .../Providers}/ProviderSettings.cs | 0 .../Providers}/ProvidersInitalizer.cs | 0 .../Screens}/IScreenProvider.cs | 0 .../Screens}/IScreenProviderSource.cs | 0 .../Screens}/ScreenRepoBuilder.cs | 0 .../Screens}/ScreenRepository.cs | 0 .../Screens}/StaticScreenProviderSource.cs | 0 .../Template}/ITemplateExtender.cs | 0 .../Template}/ScribanScreenProvider.cs | 0 .../Template}/TemplateContextProvider.cs | 0 .../Template}/TemplateInitializer.cs | 0 .../Template}/TemplateLoader.cs | 0 .../Template}/TemplateRenderer.cs | 0 .../Template}/TemplateSettings.cs | 0 .../Template}/UtilTemplateExtender.cs | 0 {Utils => DisplayUtil/Utils}/Constants.cs | 0 {Utils => DisplayUtil/Utils}/IconDrawer.cs | 0 .../Utils}/ServiceCollectionExtension.cs | 0 .../Utils}/StringExtension.cs | 0 .../Utils}/TimedScopedService.cs | 0 .../XmlModel}/DefaultDefinition.cs | 0 .../XmlModel}/Models/Flexbox.cs | 0 .../XmlModel}/Models/HBox.cs | 0 .../XmlModel}/Models/ICollectionXmlModel.cs | 0 .../XmlModel}/Models/IXmlModel.cs | 0 .../XmlModel}/Models/Icon.cs | 0 .../XmlModel}/Models/Screen.cs | 0 .../XmlModel}/Models/Text.cs | 0 .../XmlModel}/Models/VBox.cs | 0 .../XmlModel}/SerializingResult.cs | 0 .../XmlModel}/XmlLayoutDeserializer.cs | 0 .../appsettings.Development.json | 16 +-- .../appsettings.json | 18 ++-- .../wwwroot}/compression-test.html | 0 README.md | 15 ++- 68 files changed, 144 insertions(+), 131 deletions(-) rename DisplayUtil.csproj => DisplayUtil/DisplayUtil.csproj (97%) rename {EspUtilities => DisplayUtil/EspUtilities}/BinaryImageStreamCreater.cs (100%) rename {EspUtilities => DisplayUtil/EspUtilities}/EspImageProvider.cs (100%) rename {EspUtilities => DisplayUtil/EspUtilities}/EspUtilitiesInitExtension.cs (100%) rename {EspUtilities => DisplayUtil/EspUtilities}/RunLengthCompressor.cs (100%) rename {HomeAssistant => DisplayUtil/HomeAssistant}/Calendar/HassAppointmentStore.cs (100%) rename {HomeAssistant => DisplayUtil/HomeAssistant}/Calendar/HassCalendarJob.cs (100%) rename {HomeAssistant => DisplayUtil/HomeAssistant}/Calendar/HassCalendarSettings.cs (100%) rename {HomeAssistant => DisplayUtil/HomeAssistant}/Calendar/HassCalendarWorker.cs (100%) rename {HomeAssistant => DisplayUtil/HomeAssistant}/HassExtensions.cs (100%) rename {HomeAssistant => DisplayUtil/HomeAssistant}/HassHostedService.cs (100%) rename {HomeAssistant => DisplayUtil/HomeAssistant}/HassTemplateExtender.cs (100%) rename {Layouting => DisplayUtil/Layouting}/DrawManager.cs (100%) rename {Layouting => DisplayUtil/Layouting}/Element.cs (100%) rename {Layouting => DisplayUtil/Layouting}/ElementCollection.cs (100%) rename {Layouting => DisplayUtil/Layouting}/FlexboxElement.cs (100%) rename {Layouting => DisplayUtil/Layouting}/HBoxElement.cs (100%) rename {Layouting => DisplayUtil/Layouting}/IconElement.cs (100%) rename {Layouting => DisplayUtil/Layouting}/SiteSize.cs (100%) rename {Layouting => DisplayUtil/Layouting}/TextElement.cs (100%) rename {Layouting => DisplayUtil/Layouting}/VBoxElement.cs (100%) rename {MqttExport => DisplayUtil/MqttExport}/ExportingMqttClient.cs (100%) rename {MqttExport => DisplayUtil/MqttExport}/MqttExportJob.cs (100%) rename {MqttExport => DisplayUtil/MqttExport}/MqttExporter.cs (100%) rename {MqttExport => DisplayUtil/MqttExport}/MqttInitExtension.cs (100%) rename {MqttExport => DisplayUtil/MqttExport}/MqttSettings.cs (100%) rename {MqttExport => DisplayUtil/MqttExport}/MqttUrlRenderer.cs (100%) rename Program.cs => DisplayUtil/Program.cs (96%) rename {Properties => DisplayUtil/Properties}/launchSettings.json (96%) rename {Providers => DisplayUtil/Providers}/FontProvider.cs (100%) rename {Providers => DisplayUtil/Providers}/GFontDownloader.cs (100%) rename {Providers => DisplayUtil/Providers}/IconPathProvider.cs (100%) rename {Providers => DisplayUtil/Providers}/ProviderSettings.cs (100%) rename {Providers => DisplayUtil/Providers}/ProvidersInitalizer.cs (100%) rename {Screens => DisplayUtil/Screens}/IScreenProvider.cs (100%) rename {Screens => DisplayUtil/Screens}/IScreenProviderSource.cs (100%) rename {Screens => DisplayUtil/Screens}/ScreenRepoBuilder.cs (100%) rename {Screens => DisplayUtil/Screens}/ScreenRepository.cs (100%) rename {Screens => DisplayUtil/Screens}/StaticScreenProviderSource.cs (100%) rename {Template => DisplayUtil/Template}/ITemplateExtender.cs (100%) rename {Template => DisplayUtil/Template}/ScribanScreenProvider.cs (100%) rename {Template => DisplayUtil/Template}/TemplateContextProvider.cs (100%) rename {Template => DisplayUtil/Template}/TemplateInitializer.cs (100%) rename {Template => DisplayUtil/Template}/TemplateLoader.cs (100%) rename {Template => DisplayUtil/Template}/TemplateRenderer.cs (100%) rename {Template => DisplayUtil/Template}/TemplateSettings.cs (100%) rename {Template => DisplayUtil/Template}/UtilTemplateExtender.cs (100%) rename {Utils => DisplayUtil/Utils}/Constants.cs (100%) rename {Utils => DisplayUtil/Utils}/IconDrawer.cs (100%) rename {Utils => DisplayUtil/Utils}/ServiceCollectionExtension.cs (100%) rename {Utils => DisplayUtil/Utils}/StringExtension.cs (100%) rename {Utils => DisplayUtil/Utils}/TimedScopedService.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/DefaultDefinition.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/Flexbox.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/HBox.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/ICollectionXmlModel.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/IXmlModel.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/Icon.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/Screen.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/Text.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/Models/VBox.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/SerializingResult.cs (100%) rename {XmlModel => DisplayUtil/XmlModel}/XmlLayoutDeserializer.cs (100%) rename appsettings.Development.json => DisplayUtil/appsettings.Development.json (94%) rename appsettings.json => DisplayUtil/appsettings.json (94%) rename {wwwroot => DisplayUtil/wwwroot}/compression-test.html (100%) diff --git a/DisplayUtil.sln b/DisplayUtil.sln index 386a91b..729a045 100644 --- a/DisplayUtil.sln +++ b/DisplayUtil.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.5.002.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisplayUtil", "DisplayUtil.csproj", "{2DF5E15B-3151-4106-A8C2-2EB2302B2958}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisplayUtil", "DisplayUtil/DisplayUtil.csproj", "{2DF5E15B-3151-4106-A8C2-2EB2302B2958}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/DisplayUtil.csproj b/DisplayUtil/DisplayUtil.csproj similarity index 97% rename from DisplayUtil.csproj rename to DisplayUtil/DisplayUtil.csproj index e1b6234..713dd93 100644 --- a/DisplayUtil.csproj +++ b/DisplayUtil/DisplayUtil.csproj @@ -1,29 +1,29 @@ - - - - net8.0 - enable - enable - false - - - - - - - - - - - - - - - - - + + + + net8.0 + enable + enable + false + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EspUtilities/BinaryImageStreamCreater.cs b/DisplayUtil/EspUtilities/BinaryImageStreamCreater.cs similarity index 100% rename from EspUtilities/BinaryImageStreamCreater.cs rename to DisplayUtil/EspUtilities/BinaryImageStreamCreater.cs diff --git a/EspUtilities/EspImageProvider.cs b/DisplayUtil/EspUtilities/EspImageProvider.cs similarity index 100% rename from EspUtilities/EspImageProvider.cs rename to DisplayUtil/EspUtilities/EspImageProvider.cs diff --git a/EspUtilities/EspUtilitiesInitExtension.cs b/DisplayUtil/EspUtilities/EspUtilitiesInitExtension.cs similarity index 100% rename from EspUtilities/EspUtilitiesInitExtension.cs rename to DisplayUtil/EspUtilities/EspUtilitiesInitExtension.cs diff --git a/EspUtilities/RunLengthCompressor.cs b/DisplayUtil/EspUtilities/RunLengthCompressor.cs similarity index 100% rename from EspUtilities/RunLengthCompressor.cs rename to DisplayUtil/EspUtilities/RunLengthCompressor.cs diff --git a/HomeAssistant/Calendar/HassAppointmentStore.cs b/DisplayUtil/HomeAssistant/Calendar/HassAppointmentStore.cs similarity index 100% rename from HomeAssistant/Calendar/HassAppointmentStore.cs rename to DisplayUtil/HomeAssistant/Calendar/HassAppointmentStore.cs diff --git a/HomeAssistant/Calendar/HassCalendarJob.cs b/DisplayUtil/HomeAssistant/Calendar/HassCalendarJob.cs similarity index 100% rename from HomeAssistant/Calendar/HassCalendarJob.cs rename to DisplayUtil/HomeAssistant/Calendar/HassCalendarJob.cs diff --git a/HomeAssistant/Calendar/HassCalendarSettings.cs b/DisplayUtil/HomeAssistant/Calendar/HassCalendarSettings.cs similarity index 100% rename from HomeAssistant/Calendar/HassCalendarSettings.cs rename to DisplayUtil/HomeAssistant/Calendar/HassCalendarSettings.cs diff --git a/HomeAssistant/Calendar/HassCalendarWorker.cs b/DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs similarity index 100% rename from HomeAssistant/Calendar/HassCalendarWorker.cs rename to DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs diff --git a/HomeAssistant/HassExtensions.cs b/DisplayUtil/HomeAssistant/HassExtensions.cs similarity index 100% rename from HomeAssistant/HassExtensions.cs rename to DisplayUtil/HomeAssistant/HassExtensions.cs diff --git a/HomeAssistant/HassHostedService.cs b/DisplayUtil/HomeAssistant/HassHostedService.cs similarity index 100% rename from HomeAssistant/HassHostedService.cs rename to DisplayUtil/HomeAssistant/HassHostedService.cs diff --git a/HomeAssistant/HassTemplateExtender.cs b/DisplayUtil/HomeAssistant/HassTemplateExtender.cs similarity index 100% rename from HomeAssistant/HassTemplateExtender.cs rename to DisplayUtil/HomeAssistant/HassTemplateExtender.cs diff --git a/Layouting/DrawManager.cs b/DisplayUtil/Layouting/DrawManager.cs similarity index 100% rename from Layouting/DrawManager.cs rename to DisplayUtil/Layouting/DrawManager.cs diff --git a/Layouting/Element.cs b/DisplayUtil/Layouting/Element.cs similarity index 100% rename from Layouting/Element.cs rename to DisplayUtil/Layouting/Element.cs diff --git a/Layouting/ElementCollection.cs b/DisplayUtil/Layouting/ElementCollection.cs similarity index 100% rename from Layouting/ElementCollection.cs rename to DisplayUtil/Layouting/ElementCollection.cs diff --git a/Layouting/FlexboxElement.cs b/DisplayUtil/Layouting/FlexboxElement.cs similarity index 100% rename from Layouting/FlexboxElement.cs rename to DisplayUtil/Layouting/FlexboxElement.cs diff --git a/Layouting/HBoxElement.cs b/DisplayUtil/Layouting/HBoxElement.cs similarity index 100% rename from Layouting/HBoxElement.cs rename to DisplayUtil/Layouting/HBoxElement.cs diff --git a/Layouting/IconElement.cs b/DisplayUtil/Layouting/IconElement.cs similarity index 100% rename from Layouting/IconElement.cs rename to DisplayUtil/Layouting/IconElement.cs diff --git a/Layouting/SiteSize.cs b/DisplayUtil/Layouting/SiteSize.cs similarity index 100% rename from Layouting/SiteSize.cs rename to DisplayUtil/Layouting/SiteSize.cs diff --git a/Layouting/TextElement.cs b/DisplayUtil/Layouting/TextElement.cs similarity index 100% rename from Layouting/TextElement.cs rename to DisplayUtil/Layouting/TextElement.cs diff --git a/Layouting/VBoxElement.cs b/DisplayUtil/Layouting/VBoxElement.cs similarity index 100% rename from Layouting/VBoxElement.cs rename to DisplayUtil/Layouting/VBoxElement.cs diff --git a/MqttExport/ExportingMqttClient.cs b/DisplayUtil/MqttExport/ExportingMqttClient.cs similarity index 100% rename from MqttExport/ExportingMqttClient.cs rename to DisplayUtil/MqttExport/ExportingMqttClient.cs diff --git a/MqttExport/MqttExportJob.cs b/DisplayUtil/MqttExport/MqttExportJob.cs similarity index 100% rename from MqttExport/MqttExportJob.cs rename to DisplayUtil/MqttExport/MqttExportJob.cs diff --git a/MqttExport/MqttExporter.cs b/DisplayUtil/MqttExport/MqttExporter.cs similarity index 100% rename from MqttExport/MqttExporter.cs rename to DisplayUtil/MqttExport/MqttExporter.cs diff --git a/MqttExport/MqttInitExtension.cs b/DisplayUtil/MqttExport/MqttInitExtension.cs similarity index 100% rename from MqttExport/MqttInitExtension.cs rename to DisplayUtil/MqttExport/MqttInitExtension.cs diff --git a/MqttExport/MqttSettings.cs b/DisplayUtil/MqttExport/MqttSettings.cs similarity index 100% rename from MqttExport/MqttSettings.cs rename to DisplayUtil/MqttExport/MqttSettings.cs diff --git a/MqttExport/MqttUrlRenderer.cs b/DisplayUtil/MqttExport/MqttUrlRenderer.cs similarity index 100% rename from MqttExport/MqttUrlRenderer.cs rename to DisplayUtil/MqttExport/MqttUrlRenderer.cs diff --git a/Program.cs b/DisplayUtil/Program.cs similarity index 96% rename from Program.cs rename to DisplayUtil/Program.cs index 66ce944..fa97a33 100644 --- a/Program.cs +++ b/DisplayUtil/Program.cs @@ -1,51 +1,51 @@ -using DisplayUtil.EspUtilities; -using DisplayUtil.HomeAssistant; -using DisplayUtil.MqttExport; -using DisplayUtil.Providers; -using DisplayUtil.Scenes; -using DisplayUtil.Template; -using DisplayUtil.Utils; -using DisplayUtil.XmlModel; - -var builder = WebApplication.CreateBuilder(args); -builder.Configuration.AddJsonFile("appsettings.Local.json", true); - -// Add services to the container. -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); -builder - .AddProviders() - .AddTemplates() - .AddHassSupport() - .AddMqttWriter() - .AddEspUtilities(); - -builder.Services - .AddSingleton() - .AddTransient(); - -builder.Services.AddScreenProvider(o => o - .AddScribanFiles() -); - -var app = builder.Build(); - -app.UseSwagger(); -app.UseSwaggerUI(); - -app.MapGet("/preview/{providerId}", async (string providerId, ScreenRepository repo) => -{ - using var image = await repo.GetImageAsync(providerId); - using var data = image.Encode(SkiaSharp.SKEncodedImageFormat.Png, 100); - return Results.File(data.ToArray(), "image/png"); - -}) -.WithName("Preview Image") -.WithOpenApi(); - -app.UseEspUtilities() - .UseMqttWriter(); -app.UseStaticFiles(); - +using DisplayUtil.EspUtilities; +using DisplayUtil.HomeAssistant; +using DisplayUtil.MqttExport; +using DisplayUtil.Providers; +using DisplayUtil.Scenes; +using DisplayUtil.Template; +using DisplayUtil.Utils; +using DisplayUtil.XmlModel; + +var builder = WebApplication.CreateBuilder(args); +builder.Configuration.AddJsonFile("appsettings.Local.json", true); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder + .AddProviders() + .AddTemplates() + .AddHassSupport() + .AddMqttWriter() + .AddEspUtilities(); + +builder.Services + .AddSingleton() + .AddTransient(); + +builder.Services.AddScreenProvider(o => o + .AddScribanFiles() +); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(); + +app.MapGet("/preview/{providerId}", async (string providerId, ScreenRepository repo) => +{ + using var image = await repo.GetImageAsync(providerId); + using var data = image.Encode(SkiaSharp.SKEncodedImageFormat.Png, 100); + return Results.File(data.ToArray(), "image/png"); + +}) +.WithName("Preview Image") +.WithOpenApi(); + +app.UseEspUtilities() + .UseMqttWriter(); +app.UseStaticFiles(); + app.Run(); \ No newline at end of file diff --git a/Properties/launchSettings.json b/DisplayUtil/Properties/launchSettings.json similarity index 96% rename from Properties/launchSettings.json rename to DisplayUtil/Properties/launchSettings.json index d6c1c45..3109202 100644 --- a/Properties/launchSettings.json +++ b/DisplayUtil/Properties/launchSettings.json @@ -1,35 +1,35 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51180", - "sslPort": 44304 - } - }, - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "applicationUrl": "http://0.0.0.0:5296", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7133;http://0.0.0.0:5296", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51180", + "sslPort": 44304 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "http://0.0.0.0:5296", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7133;http://0.0.0.0:5296", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } } \ No newline at end of file diff --git a/Providers/FontProvider.cs b/DisplayUtil/Providers/FontProvider.cs similarity index 100% rename from Providers/FontProvider.cs rename to DisplayUtil/Providers/FontProvider.cs diff --git a/Providers/GFontDownloader.cs b/DisplayUtil/Providers/GFontDownloader.cs similarity index 100% rename from Providers/GFontDownloader.cs rename to DisplayUtil/Providers/GFontDownloader.cs diff --git a/Providers/IconPathProvider.cs b/DisplayUtil/Providers/IconPathProvider.cs similarity index 100% rename from Providers/IconPathProvider.cs rename to DisplayUtil/Providers/IconPathProvider.cs diff --git a/Providers/ProviderSettings.cs b/DisplayUtil/Providers/ProviderSettings.cs similarity index 100% rename from Providers/ProviderSettings.cs rename to DisplayUtil/Providers/ProviderSettings.cs diff --git a/Providers/ProvidersInitalizer.cs b/DisplayUtil/Providers/ProvidersInitalizer.cs similarity index 100% rename from Providers/ProvidersInitalizer.cs rename to DisplayUtil/Providers/ProvidersInitalizer.cs diff --git a/Screens/IScreenProvider.cs b/DisplayUtil/Screens/IScreenProvider.cs similarity index 100% rename from Screens/IScreenProvider.cs rename to DisplayUtil/Screens/IScreenProvider.cs diff --git a/Screens/IScreenProviderSource.cs b/DisplayUtil/Screens/IScreenProviderSource.cs similarity index 100% rename from Screens/IScreenProviderSource.cs rename to DisplayUtil/Screens/IScreenProviderSource.cs diff --git a/Screens/ScreenRepoBuilder.cs b/DisplayUtil/Screens/ScreenRepoBuilder.cs similarity index 100% rename from Screens/ScreenRepoBuilder.cs rename to DisplayUtil/Screens/ScreenRepoBuilder.cs diff --git a/Screens/ScreenRepository.cs b/DisplayUtil/Screens/ScreenRepository.cs similarity index 100% rename from Screens/ScreenRepository.cs rename to DisplayUtil/Screens/ScreenRepository.cs diff --git a/Screens/StaticScreenProviderSource.cs b/DisplayUtil/Screens/StaticScreenProviderSource.cs similarity index 100% rename from Screens/StaticScreenProviderSource.cs rename to DisplayUtil/Screens/StaticScreenProviderSource.cs diff --git a/Template/ITemplateExtender.cs b/DisplayUtil/Template/ITemplateExtender.cs similarity index 100% rename from Template/ITemplateExtender.cs rename to DisplayUtil/Template/ITemplateExtender.cs diff --git a/Template/ScribanScreenProvider.cs b/DisplayUtil/Template/ScribanScreenProvider.cs similarity index 100% rename from Template/ScribanScreenProvider.cs rename to DisplayUtil/Template/ScribanScreenProvider.cs diff --git a/Template/TemplateContextProvider.cs b/DisplayUtil/Template/TemplateContextProvider.cs similarity index 100% rename from Template/TemplateContextProvider.cs rename to DisplayUtil/Template/TemplateContextProvider.cs diff --git a/Template/TemplateInitializer.cs b/DisplayUtil/Template/TemplateInitializer.cs similarity index 100% rename from Template/TemplateInitializer.cs rename to DisplayUtil/Template/TemplateInitializer.cs diff --git a/Template/TemplateLoader.cs b/DisplayUtil/Template/TemplateLoader.cs similarity index 100% rename from Template/TemplateLoader.cs rename to DisplayUtil/Template/TemplateLoader.cs diff --git a/Template/TemplateRenderer.cs b/DisplayUtil/Template/TemplateRenderer.cs similarity index 100% rename from Template/TemplateRenderer.cs rename to DisplayUtil/Template/TemplateRenderer.cs diff --git a/Template/TemplateSettings.cs b/DisplayUtil/Template/TemplateSettings.cs similarity index 100% rename from Template/TemplateSettings.cs rename to DisplayUtil/Template/TemplateSettings.cs diff --git a/Template/UtilTemplateExtender.cs b/DisplayUtil/Template/UtilTemplateExtender.cs similarity index 100% rename from Template/UtilTemplateExtender.cs rename to DisplayUtil/Template/UtilTemplateExtender.cs diff --git a/Utils/Constants.cs b/DisplayUtil/Utils/Constants.cs similarity index 100% rename from Utils/Constants.cs rename to DisplayUtil/Utils/Constants.cs diff --git a/Utils/IconDrawer.cs b/DisplayUtil/Utils/IconDrawer.cs similarity index 100% rename from Utils/IconDrawer.cs rename to DisplayUtil/Utils/IconDrawer.cs diff --git a/Utils/ServiceCollectionExtension.cs b/DisplayUtil/Utils/ServiceCollectionExtension.cs similarity index 100% rename from Utils/ServiceCollectionExtension.cs rename to DisplayUtil/Utils/ServiceCollectionExtension.cs diff --git a/Utils/StringExtension.cs b/DisplayUtil/Utils/StringExtension.cs similarity index 100% rename from Utils/StringExtension.cs rename to DisplayUtil/Utils/StringExtension.cs diff --git a/Utils/TimedScopedService.cs b/DisplayUtil/Utils/TimedScopedService.cs similarity index 100% rename from Utils/TimedScopedService.cs rename to DisplayUtil/Utils/TimedScopedService.cs diff --git a/XmlModel/DefaultDefinition.cs b/DisplayUtil/XmlModel/DefaultDefinition.cs similarity index 100% rename from XmlModel/DefaultDefinition.cs rename to DisplayUtil/XmlModel/DefaultDefinition.cs diff --git a/XmlModel/Models/Flexbox.cs b/DisplayUtil/XmlModel/Models/Flexbox.cs similarity index 100% rename from XmlModel/Models/Flexbox.cs rename to DisplayUtil/XmlModel/Models/Flexbox.cs diff --git a/XmlModel/Models/HBox.cs b/DisplayUtil/XmlModel/Models/HBox.cs similarity index 100% rename from XmlModel/Models/HBox.cs rename to DisplayUtil/XmlModel/Models/HBox.cs diff --git a/XmlModel/Models/ICollectionXmlModel.cs b/DisplayUtil/XmlModel/Models/ICollectionXmlModel.cs similarity index 100% rename from XmlModel/Models/ICollectionXmlModel.cs rename to DisplayUtil/XmlModel/Models/ICollectionXmlModel.cs diff --git a/XmlModel/Models/IXmlModel.cs b/DisplayUtil/XmlModel/Models/IXmlModel.cs similarity index 100% rename from XmlModel/Models/IXmlModel.cs rename to DisplayUtil/XmlModel/Models/IXmlModel.cs diff --git a/XmlModel/Models/Icon.cs b/DisplayUtil/XmlModel/Models/Icon.cs similarity index 100% rename from XmlModel/Models/Icon.cs rename to DisplayUtil/XmlModel/Models/Icon.cs diff --git a/XmlModel/Models/Screen.cs b/DisplayUtil/XmlModel/Models/Screen.cs similarity index 100% rename from XmlModel/Models/Screen.cs rename to DisplayUtil/XmlModel/Models/Screen.cs diff --git a/XmlModel/Models/Text.cs b/DisplayUtil/XmlModel/Models/Text.cs similarity index 100% rename from XmlModel/Models/Text.cs rename to DisplayUtil/XmlModel/Models/Text.cs diff --git a/XmlModel/Models/VBox.cs b/DisplayUtil/XmlModel/Models/VBox.cs similarity index 100% rename from XmlModel/Models/VBox.cs rename to DisplayUtil/XmlModel/Models/VBox.cs diff --git a/XmlModel/SerializingResult.cs b/DisplayUtil/XmlModel/SerializingResult.cs similarity index 100% rename from XmlModel/SerializingResult.cs rename to DisplayUtil/XmlModel/SerializingResult.cs diff --git a/XmlModel/XmlLayoutDeserializer.cs b/DisplayUtil/XmlModel/XmlLayoutDeserializer.cs similarity index 100% rename from XmlModel/XmlLayoutDeserializer.cs rename to DisplayUtil/XmlModel/XmlLayoutDeserializer.cs diff --git a/appsettings.Development.json b/DisplayUtil/appsettings.Development.json similarity index 94% rename from appsettings.Development.json rename to DisplayUtil/appsettings.Development.json index 5bce292..8dd46c7 100644 --- a/appsettings.Development.json +++ b/DisplayUtil/appsettings.Development.json @@ -1,9 +1,9 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning", - "DisplayUtil": "Debug" - } - } +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "DisplayUtil": "Debug" + } + } } \ No newline at end of file diff --git a/appsettings.json b/DisplayUtil/appsettings.json similarity index 94% rename from appsettings.json rename to DisplayUtil/appsettings.json index 4d56694..10f68b8 100644 --- a/appsettings.json +++ b/DisplayUtil/appsettings.json @@ -1,9 +1,9 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -} +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/wwwroot/compression-test.html b/DisplayUtil/wwwroot/compression-test.html similarity index 100% rename from wwwroot/compression-test.html rename to DisplayUtil/wwwroot/compression-test.html diff --git a/README.md b/README.md index 26e6958..201f9e2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,17 @@ # DisplayUtil DisplayUtil is a C# webserver for building status screens for an ESP32 with a -epaper display. \ No newline at end of file +epaper display. + +## Features + + - Rendering an XML Template with [Scriban](https://github.com/scriban/scriban) + - Apply an XML document to a layouting system + - Apply the layout system on an [SkiaSharp](https://github.com/mono/SkiaSharp) canvas + - Compress the image for the two color eInk display + +## Main idea + +Rendering an image where I have massive power and submit it to the display. +Since I'm using ESPHome, I send a URL over MQTT and the ESP fetches the image +over HTTP. \ No newline at end of file From fe57dbbe534f70e71a1cd53da7f59bcab1957224 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 16:47:16 +0200 Subject: [PATCH 04/23] Fix remaining folders --- .github/workflows/containerize.yml | 19 +++++++++++-------- .vscode/launch.json | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/containerize.yml b/.github/workflows/containerize.yml index df0b23d..906973c 100644 --- a/.github/workflows/containerize.yml +++ b/.github/workflows/containerize.yml @@ -10,23 +10,26 @@ jobs: runs-on: ubuntu-22.04 env: NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages + defaults: + run: + working-directory: ./DisplayUtil steps: - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3.2.0 + uses: actions/setup-dotnet@v4 with: dotnet-version: "8" cache: true - cache-dependency-path: DisplayUtil.csproj + cache-dependency-path: DisplayUtil/DisplayUtil.csproj - name: Restore NuGet dependencies run: dotnet restore --locked-mode - name: Build server run: dotnet publish -c Release --no-restore - name: Upload a Build Artifact - uses: actions/upload-artifact@v3.1.3 + uses: actions/upload-artifact@v4 with: name: backend-artifacts - path: bin/Release/net8.0/publish/ + path: DisplayUtil/bin/Release/net8.0/publish/ build-docker-image: runs-on: ubuntu-22.04 permissions: @@ -37,23 +40,23 @@ jobs: steps: - uses: actions/checkout@v4 - name: Download build artifacts - uses: actions/download-artifact@v3.0.2 + uses: actions/download-artifact@v4 with: name: backend-artifacts path: build/ - name: Docker Login - uses: docker/login-action@v3.0.0 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Docker Metadata action id: docker_metadata - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ github.repository }} - name: Build and push Docker image - uses: docker/build-push-action@v5.1.0 + uses: docker/build-push-action@v5 with: context: . push: true diff --git a/.vscode/launch.json b/.vscode/launch.json index 2d667ea..8df1f03 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,7 @@ "name": "C#: DisplayUtil [https]", "type": "dotnet", "request": "launch", - "projectPath": "${workspaceFolder}\\DisplayUtil.csproj", + "projectPath": "${workspaceFolder}\\DisplayUtil\\DisplayUtil.csproj", "launchConfigurationId": "TargetFramework=;https", "logging": { "moduleLoad": false From b321626620388f8306039736ae5437a3441e6665 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 16:53:11 +0200 Subject: [PATCH 05/23] Add ESPHome elements --- components/mqtt_screen/__init__.py | 38 +++++ components/mqtt_screen/mqtt_screen.cpp | 202 +++++++++++++++++++++++++ components/mqtt_screen/mqtt_screen.h | 74 +++++++++ 3 files changed, 314 insertions(+) create mode 100644 components/mqtt_screen/__init__.py create mode 100644 components/mqtt_screen/mqtt_screen.cpp create mode 100644 components/mqtt_screen/mqtt_screen.h diff --git a/components/mqtt_screen/__init__.py b/components/mqtt_screen/__init__.py new file mode 100644 index 0000000..b3b31c7 --- /dev/null +++ b/components/mqtt_screen/__init__.py @@ -0,0 +1,38 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import mqtt, display, http_request +from esphome.const import CONF_ID + +DEPENDENCIES = ['mqtt', 'display', 'http_request'] + +AUTO_LOAD = ["mqtt_screen"] + +mqtt_ns = cg.esphome_ns.namespace("mqtt_screen") + +MqttScreen = mqtt_ns.class_("MqttScreen", cg.Component) + +CONF_CLIENT = "client" +CONF_SCREEN = "display" +CONF_TOPIC = "topic" +CONF_HTTP_CLIENT = "http_client" + +CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(MqttScreen), + cv.Required(CONF_CLIENT): cv.use_id(mqtt.MQTTClientComponent), + cv.Required(CONF_SCREEN): cv.use_id(display.DisplayBuffer), + cv.Required(CONF_HTTP_CLIENT): cv.use_id(http_request.HttpRequestComponent), + cv.Required(CONF_TOPIC): cv.string +}) + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + screen = await cg.get_variable(config[CONF_SCREEN]) + mqttClient = await cg.get_variable(config[CONF_CLIENT]) + httpClient = await cg.get_variable(config[CONF_HTTP_CLIENT]) + + cg.add(var.set_topic(config[CONF_TOPIC])) + cg.add(var.set_mqtt(mqttClient)) + cg.add(var.set_display(screen)) + cg.add(var.set_http_client(httpClient)) + + await cg.register_component(var, config) \ No newline at end of file diff --git a/components/mqtt_screen/mqtt_screen.cpp b/components/mqtt_screen/mqtt_screen.cpp new file mode 100644 index 0000000..aab8f33 --- /dev/null +++ b/components/mqtt_screen/mqtt_screen.cpp @@ -0,0 +1,202 @@ +#include +#include "mqtt_screen.h" +#include "esphome/core/log.h" +#include + +using namespace esphome; + +namespace esphome +{ + namespace mqtt_screen + { + inline void Position::add_pixels(int quantity) + { + _x += quantity; + + while (_x >= _width) + { + _x -= _width; + _y++; + } + + if (_y > _height) + _y = _height; + } + + static const char *const TAG = "mqtt_screen"; + + std::vector *base64_decode(std::string const &encoded_string) + { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in = 0; + char char_array_4[4], char_array_3[3]; + auto ret = new std::vector(); + + while (in_len-- && (encoded_string[in] != '=') && is_base64(encoded_string[in])) + { + char_array_4[i++] = encoded_string[in]; + in++; + if (i == 4) + { + for (i = 0; i < 4; i++) + char_array_4[i] = BASE64_CHARS.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret->push_back(char_array_3[i]); + i = 0; + } + } + + if (i) + { + for (j = i; j < 4; j++) + char_array_4[j] = 0; + + for (j = 0; j < 4; j++) + char_array_4[j] = BASE64_CHARS.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) + ret->push_back(char_array_3[j]); + } + + return ret; + } + + void MqttScreen::dump_config() + { + ESP_LOGCONFIG(TAG, " Topic: %s", this->topic_.c_str()); + } + + void MqttScreen::process(int32_t status_code, uint32_t duration_ms) + { + ESP_LOGI("mqtt_screen", "Process HTTP Response with SC: %d", status_code); + + if (status_code >= 300 || status_code < 200 || data_ != nullptr) + return; + + // Get Response + auto string_response = http_client_->get_string(); + auto string_object = new std::string(string_response); + + // Decode Base64 + ESP_LOGI("mqtt_screen", "Decode Base64"); + auto decodedData = base64_decode(*string_object); + delete string_object; + + data_ = new std::vector; + + for (size_t i = 0; i < decodedData->size(); i += 2) + { + if (i + 1 < decodedData->size()) + { + auto highByte = static_cast((*decodedData)[i + 1]); + auto lowByte = static_cast((*decodedData)[i]); + unsigned short value = (highByte << 8) | lowByte; + data_->push_back(value); + } + } + delete decodedData; + + // Update Display + ESP_LOGI("mqtt_screen", "Update Screen"); + this->display_->update(); + + delete data_; + data_ = nullptr; + } + + void MqttScreen::setup() + { + mqtt_client_->subscribe( + topic_, + std::bind(&MqttScreen::on_message, this, std::placeholders::_1, std::placeholders::_2)); + + display_->set_writer( + std::bind(&MqttScreen::draw, this, std::placeholders::_1)); + + trigger_ = new http_request::HttpRequestResponseTrigger(); + auto automation = new Automation(trigger_); + auto lambdaaction = new LambdaAction( + std::bind(&MqttScreen::process, this, std::placeholders::_1, std::placeholders::_2)); + automation->add_actions({lambdaaction}); + } + + void MqttScreen::on_message(const std::string &topic, const std::string &message) + { + ESP_LOGI("mqtt_screen", "Received MQTT URL %s", message.c_str()); + + if (data_ != nullptr) + { + ESP_LOGI("mqtt_screen", "Already running an action. Skipping"); + return; + } + + http_client_->set_method("GET"); + http_client_->set_url(message); + + ESP_LOGI("mqtt_screen", "Request Payload from HTTP"); + http_client_->send({trigger_}); + ESP_LOGI("mqtt_screen", "Finished HTTP"); + } + + void MqttScreen::draw(display::Display &display) + { + display.fill(display::COLOR_OFF); + ESP_LOGI("mqtt_screen", "Begin drawing"); + + Position pos(display.get_width(), display.get_height()); + + for (std::vector::iterator it = data_->begin(); it != data_->end(); ++it) + { + unsigned short plain_data = *it; + unsigned short mode = plain_data >> 14; + unsigned short payload = plain_data & ~(3 << 14); + + if (mode == 0) + { + // Plain + for (int i = 13; i >= 0; i--) + { + unsigned short mask = 1 << i; + if (payload & mask) + { + display.draw_pixel_at(pos.get_x(), pos.get_y(), display::COLOR_ON); + } + pos++; + } + } + else if (mode == 1) + { + // White (nothing to draw) + pos += payload; + } + else if (mode == 2) + { + // Black + for (int i = payload; i > 0; i--) + { + display.draw_pixel_at(pos.get_x(), pos.get_y(), display::COLOR_ON); + pos++; + } + } + } + + ESP_LOGI("mqtt_screen", "Finished drawing"); + } + + float MqttScreen::get_setup_priority() const + { + return setup_priority::AFTER_CONNECTION; + } + } +} \ No newline at end of file diff --git a/components/mqtt_screen/mqtt_screen.h b/components/mqtt_screen/mqtt_screen.h new file mode 100644 index 0000000..9e0f79b --- /dev/null +++ b/components/mqtt_screen/mqtt_screen.h @@ -0,0 +1,74 @@ +#pragma once + +#include "esphome.h" +using namespace esphome; + +namespace esphome +{ + namespace mqtt_screen + { + + static const std::string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); } + + std::vector *base64_decode(std::string const &); + + class Position { + public: + Position(int width, int height) { + _width = width; + _height = height; + }; + inline int get_x(){ return _x + 1; }; + inline int get_y(){ return _y + 1; }; + void add_pixels(int quantity); + inline Position operator++(int i) { + add_pixels(1); + return *this; + }; + inline Position& operator +=(const int& quantity) { + add_pixels(quantity); + return *this; + }; + + private: + int _x{0}; + int _y{0}; + int _width; + int _height; + }; + + class MqttScreen : public Component + { + public: + void set_topic(const std::string &topic) { topic_ = topic; } + void set_mqtt(mqtt::MQTTClientComponent *mqttClient) { mqtt_client_ = mqttClient; } + void set_display(display::Display *display) { display_ = display; } + void set_http_client(http_request::HttpRequestComponent *http_client) { + http_client_ = http_client; + } + + void process(int32_t status_code, uint32_t duration_ms); + + void on_message(const std::string &topic, const std::string &message); + void draw(display::Display &display); + + void setup() override; + void loop() override{}; + void dump_config() override; + float get_setup_priority() const override; + + private: + std::vector* data_ {nullptr}; + + http_request::HttpRequestResponseTrigger *trigger_; + display::Display *display_; + mqtt::MQTTClientComponent *mqtt_client_; + http_request::HttpRequestComponent *http_client_; + std::string topic_; + }; + } +} \ No newline at end of file From 9b3bfbd5b1161a0ceceffedd9c6bc17e8f06e765 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 19:38:35 +0200 Subject: [PATCH 06/23] Add example --- .gitignore | 3 ++- DisplayUtil/appsettings.Development.json | 11 +++++++++++ examples/hello.sbntxt | 11 +++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 examples/hello.sbntxt diff --git a/.gitignore b/.gitignore index 7cc8f33..4e69a60 100644 --- a/.gitignore +++ b/.gitignore @@ -483,4 +483,5 @@ $RECYCLE.BIN/ # Vim temporary swap files *.swp appsettings.Local.json -Resources/* \ No newline at end of file +Resources/* +*.ttf \ No newline at end of file diff --git a/DisplayUtil/appsettings.Development.json b/DisplayUtil/appsettings.Development.json index 8dd46c7..1ed17aa 100644 --- a/DisplayUtil/appsettings.Development.json +++ b/DisplayUtil/appsettings.Development.json @@ -5,5 +5,16 @@ "Microsoft.AspNetCore": "Warning", "DisplayUtil": "Debug" } + }, + "Providers": { + "Fonts": { + "Roboto400": "gfonts://Roboto@400" + }, + "GoogleFontsCachePath": "../examples" + }, + "Templates": { + "Paths": { + "example": "../examples" + } } } \ No newline at end of file diff --git a/examples/hello.sbntxt b/examples/hello.sbntxt new file mode 100644 index 0000000..ad9a83a --- /dev/null +++ b/examples/hello.sbntxt @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From 2925094d491154f6629962ee40abeaa9fad98964 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 19:38:45 +0200 Subject: [PATCH 07/23] Extend Markdown --- README.md | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 201f9e2..bc7ed89 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,30 @@ # DisplayUtil -DisplayUtil is a C# webserver for building status screens for an ESP32 with a +DisplayUtil is a C# web server for creating status screens for an ESP32 with an epaper display. ## Features - - Rendering an XML Template with [Scriban](https://github.com/scriban/scriban) - - Apply an XML document to a layouting system - - Apply the layout system on an [SkiaSharp](https://github.com/mono/SkiaSharp) canvas - - Compress the image for the two color eInk display + - Render an XML template with [Scriban](https://github.com/scriban/scriban) + - Applying an XML document to a layout system + - Applying the layout system to a [SkiaSharp](https://github.com/mono/SkiaSharp) canvas + - Compressing the image for the two-color eInk display ## Main idea -Rendering an image where I have massive power and submit it to the display. -Since I'm using ESPHome, I send a URL over MQTT and the ESP fetches the image -over HTTP. \ No newline at end of file +Render an image where I have massive power and submit it to the display. +Since I'm using ESPHome, I send a URL over MQTT and the ESP fetches the image over HTTP. +over HTTP. + +## Project structure + + - components/mqtt_screen: ESPHome implementation for image handling + - DisplayUtil/EspUtil: Converting images to two-color images and ESP compression + - DisplayUtil/HomeAssistant: utilities for working with HomeAssistant entities + - DisplayUtil/Layouting: Layouting system + - DisplayUtil/MqttExport: Export URL to MQTT + - DisplayUtil/Providers: various providers (for fonts & svg icons) + - DisplayUtil/Screens: Utilities for screen management (various outputs) + - DisplayUtil/Template: Utilities for handling Scriban templates and their rendering + - DisplayUtil/Utils: various utils + - DisplayUtil/XmlModel: Deserialization of XML content \ No newline at end of file From 4ce192f66623cef99fb468472d709f12f77e9c8f Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Tue, 16 Apr 2024 22:04:47 +0200 Subject: [PATCH 08/23] Drop esp part- other repo --- README.md | 7 +- components/mqtt_screen/__init__.py | 38 ----- components/mqtt_screen/mqtt_screen.cpp | 202 ------------------------- components/mqtt_screen/mqtt_screen.h | 74 --------- 4 files changed, 4 insertions(+), 317 deletions(-) delete mode 100644 components/mqtt_screen/__init__.py delete mode 100644 components/mqtt_screen/mqtt_screen.cpp delete mode 100644 components/mqtt_screen/mqtt_screen.h diff --git a/README.md b/README.md index bc7ed89..1772274 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ DisplayUtil is a C# web server for creating status screens for an ESP32 with an epaper display. -## Features +## Features (of server) - Render an XML template with [Scriban](https://github.com/scriban/scriban) - Applying an XML document to a layout system @@ -14,7 +14,6 @@ epaper display. Render an image where I have massive power and submit it to the display. Since I'm using ESPHome, I send a URL over MQTT and the ESP fetches the image over HTTP. -over HTTP. ## Project structure @@ -27,4 +26,6 @@ over HTTP. - DisplayUtil/Screens: Utilities for screen management (various outputs) - DisplayUtil/Template: Utilities for handling Scriban templates and their rendering - DisplayUtil/Utils: various utils - - DisplayUtil/XmlModel: Deserialization of XML content \ No newline at end of file + - DisplayUtil/XmlModel: Deserialization of XML content + +## Setup diff --git a/components/mqtt_screen/__init__.py b/components/mqtt_screen/__init__.py deleted file mode 100644 index b3b31c7..0000000 --- a/components/mqtt_screen/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import mqtt, display, http_request -from esphome.const import CONF_ID - -DEPENDENCIES = ['mqtt', 'display', 'http_request'] - -AUTO_LOAD = ["mqtt_screen"] - -mqtt_ns = cg.esphome_ns.namespace("mqtt_screen") - -MqttScreen = mqtt_ns.class_("MqttScreen", cg.Component) - -CONF_CLIENT = "client" -CONF_SCREEN = "display" -CONF_TOPIC = "topic" -CONF_HTTP_CLIENT = "http_client" - -CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MqttScreen), - cv.Required(CONF_CLIENT): cv.use_id(mqtt.MQTTClientComponent), - cv.Required(CONF_SCREEN): cv.use_id(display.DisplayBuffer), - cv.Required(CONF_HTTP_CLIENT): cv.use_id(http_request.HttpRequestComponent), - cv.Required(CONF_TOPIC): cv.string -}) - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - screen = await cg.get_variable(config[CONF_SCREEN]) - mqttClient = await cg.get_variable(config[CONF_CLIENT]) - httpClient = await cg.get_variable(config[CONF_HTTP_CLIENT]) - - cg.add(var.set_topic(config[CONF_TOPIC])) - cg.add(var.set_mqtt(mqttClient)) - cg.add(var.set_display(screen)) - cg.add(var.set_http_client(httpClient)) - - await cg.register_component(var, config) \ No newline at end of file diff --git a/components/mqtt_screen/mqtt_screen.cpp b/components/mqtt_screen/mqtt_screen.cpp deleted file mode 100644 index aab8f33..0000000 --- a/components/mqtt_screen/mqtt_screen.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include -#include "mqtt_screen.h" -#include "esphome/core/log.h" -#include - -using namespace esphome; - -namespace esphome -{ - namespace mqtt_screen - { - inline void Position::add_pixels(int quantity) - { - _x += quantity; - - while (_x >= _width) - { - _x -= _width; - _y++; - } - - if (_y > _height) - _y = _height; - } - - static const char *const TAG = "mqtt_screen"; - - std::vector *base64_decode(std::string const &encoded_string) - { - int in_len = encoded_string.size(); - int i = 0; - int j = 0; - int in = 0; - char char_array_4[4], char_array_3[3]; - auto ret = new std::vector(); - - while (in_len-- && (encoded_string[in] != '=') && is_base64(encoded_string[in])) - { - char_array_4[i++] = encoded_string[in]; - in++; - if (i == 4) - { - for (i = 0; i < 4; i++) - char_array_4[i] = BASE64_CHARS.find(char_array_4[i]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; (i < 3); i++) - ret->push_back(char_array_3[i]); - i = 0; - } - } - - if (i) - { - for (j = i; j < 4; j++) - char_array_4[j] = 0; - - for (j = 0; j < 4; j++) - char_array_4[j] = BASE64_CHARS.find(char_array_4[j]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (j = 0; (j < i - 1); j++) - ret->push_back(char_array_3[j]); - } - - return ret; - } - - void MqttScreen::dump_config() - { - ESP_LOGCONFIG(TAG, " Topic: %s", this->topic_.c_str()); - } - - void MqttScreen::process(int32_t status_code, uint32_t duration_ms) - { - ESP_LOGI("mqtt_screen", "Process HTTP Response with SC: %d", status_code); - - if (status_code >= 300 || status_code < 200 || data_ != nullptr) - return; - - // Get Response - auto string_response = http_client_->get_string(); - auto string_object = new std::string(string_response); - - // Decode Base64 - ESP_LOGI("mqtt_screen", "Decode Base64"); - auto decodedData = base64_decode(*string_object); - delete string_object; - - data_ = new std::vector; - - for (size_t i = 0; i < decodedData->size(); i += 2) - { - if (i + 1 < decodedData->size()) - { - auto highByte = static_cast((*decodedData)[i + 1]); - auto lowByte = static_cast((*decodedData)[i]); - unsigned short value = (highByte << 8) | lowByte; - data_->push_back(value); - } - } - delete decodedData; - - // Update Display - ESP_LOGI("mqtt_screen", "Update Screen"); - this->display_->update(); - - delete data_; - data_ = nullptr; - } - - void MqttScreen::setup() - { - mqtt_client_->subscribe( - topic_, - std::bind(&MqttScreen::on_message, this, std::placeholders::_1, std::placeholders::_2)); - - display_->set_writer( - std::bind(&MqttScreen::draw, this, std::placeholders::_1)); - - trigger_ = new http_request::HttpRequestResponseTrigger(); - auto automation = new Automation(trigger_); - auto lambdaaction = new LambdaAction( - std::bind(&MqttScreen::process, this, std::placeholders::_1, std::placeholders::_2)); - automation->add_actions({lambdaaction}); - } - - void MqttScreen::on_message(const std::string &topic, const std::string &message) - { - ESP_LOGI("mqtt_screen", "Received MQTT URL %s", message.c_str()); - - if (data_ != nullptr) - { - ESP_LOGI("mqtt_screen", "Already running an action. Skipping"); - return; - } - - http_client_->set_method("GET"); - http_client_->set_url(message); - - ESP_LOGI("mqtt_screen", "Request Payload from HTTP"); - http_client_->send({trigger_}); - ESP_LOGI("mqtt_screen", "Finished HTTP"); - } - - void MqttScreen::draw(display::Display &display) - { - display.fill(display::COLOR_OFF); - ESP_LOGI("mqtt_screen", "Begin drawing"); - - Position pos(display.get_width(), display.get_height()); - - for (std::vector::iterator it = data_->begin(); it != data_->end(); ++it) - { - unsigned short plain_data = *it; - unsigned short mode = plain_data >> 14; - unsigned short payload = plain_data & ~(3 << 14); - - if (mode == 0) - { - // Plain - for (int i = 13; i >= 0; i--) - { - unsigned short mask = 1 << i; - if (payload & mask) - { - display.draw_pixel_at(pos.get_x(), pos.get_y(), display::COLOR_ON); - } - pos++; - } - } - else if (mode == 1) - { - // White (nothing to draw) - pos += payload; - } - else if (mode == 2) - { - // Black - for (int i = payload; i > 0; i--) - { - display.draw_pixel_at(pos.get_x(), pos.get_y(), display::COLOR_ON); - pos++; - } - } - } - - ESP_LOGI("mqtt_screen", "Finished drawing"); - } - - float MqttScreen::get_setup_priority() const - { - return setup_priority::AFTER_CONNECTION; - } - } -} \ No newline at end of file diff --git a/components/mqtt_screen/mqtt_screen.h b/components/mqtt_screen/mqtt_screen.h deleted file mode 100644 index 9e0f79b..0000000 --- a/components/mqtt_screen/mqtt_screen.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include "esphome.h" -using namespace esphome; - -namespace esphome -{ - namespace mqtt_screen - { - - static const std::string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); } - - std::vector *base64_decode(std::string const &); - - class Position { - public: - Position(int width, int height) { - _width = width; - _height = height; - }; - inline int get_x(){ return _x + 1; }; - inline int get_y(){ return _y + 1; }; - void add_pixels(int quantity); - inline Position operator++(int i) { - add_pixels(1); - return *this; - }; - inline Position& operator +=(const int& quantity) { - add_pixels(quantity); - return *this; - }; - - private: - int _x{0}; - int _y{0}; - int _width; - int _height; - }; - - class MqttScreen : public Component - { - public: - void set_topic(const std::string &topic) { topic_ = topic; } - void set_mqtt(mqtt::MQTTClientComponent *mqttClient) { mqtt_client_ = mqttClient; } - void set_display(display::Display *display) { display_ = display; } - void set_http_client(http_request::HttpRequestComponent *http_client) { - http_client_ = http_client; - } - - void process(int32_t status_code, uint32_t duration_ms); - - void on_message(const std::string &topic, const std::string &message); - void draw(display::Display &display); - - void setup() override; - void loop() override{}; - void dump_config() override; - float get_setup_priority() const override; - - private: - std::vector* data_ {nullptr}; - - http_request::HttpRequestResponseTrigger *trigger_; - display::Display *display_; - mqtt::MQTTClientComponent *mqtt_client_; - http_request::HttpRequestComponent *http_client_; - std::string topic_; - }; - } -} \ No newline at end of file From 32c382a73f60862457b2af929a5febdd824735b3 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Mon, 22 Apr 2024 18:33:51 +0200 Subject: [PATCH 09/23] Rename method --- DisplayUtil/HomeAssistant/HassTemplateExtender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DisplayUtil/HomeAssistant/HassTemplateExtender.cs b/DisplayUtil/HomeAssistant/HassTemplateExtender.cs index bc6b143..5c5ead6 100644 --- a/DisplayUtil/HomeAssistant/HassTemplateExtender.cs +++ b/DisplayUtil/HomeAssistant/HassTemplateExtender.cs @@ -15,7 +15,7 @@ public void Enrich(ScriptObject context, EnrichScope scope) hassObject.Import("get_state", GetState); hassObject.Import("get_attribute", GetAttribute); hassObject.Import("get_float_state", GetFloatState); - hassObject.Import("get_datetime", GetDateTime); + hassObject.Import("get_datetime_state", GetDateTime); context.Add("hass", hassObject); } From ada72ea4cc2dc3d1594603cd77b3630300042a49 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Mon, 22 Apr 2024 18:33:57 +0200 Subject: [PATCH 10/23] Extend README --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 1772274..e440c77 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,63 @@ # DisplayUtil -DisplayUtil is a C# web server for creating status screens for an ESP32 with an +DisplayUtil is a C# web server for creating status screens for an ESP32 with an epaper display. ## Features (of server) - - Render an XML template with [Scriban](https://github.com/scriban/scriban) - - Applying an XML document to a layout system - - Applying the layout system to a [SkiaSharp](https://github.com/mono/SkiaSharp) canvas - - Compressing the image for the two-color eInk display +- Render an XML template with [Scriban](https://github.com/scriban/scriban) +- Applying an XML document to a layout system +- Applying the layout system to a [SkiaSharp](https://github.com/mono/SkiaSharp) canvas +- Compressing the image for the two-color eInk display ## Main idea Render an image where I have massive power and submit it to the display. Since I'm using ESPHome, I send a URL over MQTT and the ESP fetches the image over HTTP. +You can find the [ESPHome implementation here](https://github.com/timia2109/esphome_http_screen) ## Project structure - - components/mqtt_screen: ESPHome implementation for image handling - - DisplayUtil/EspUtil: Converting images to two-color images and ESP compression - - DisplayUtil/HomeAssistant: utilities for working with HomeAssistant entities - - DisplayUtil/Layouting: Layouting system - - DisplayUtil/MqttExport: Export URL to MQTT - - DisplayUtil/Providers: various providers (for fonts & svg icons) - - DisplayUtil/Screens: Utilities for screen management (various outputs) - - DisplayUtil/Template: Utilities for handling Scriban templates and their rendering - - DisplayUtil/Utils: various utils - - DisplayUtil/XmlModel: Deserialization of XML content - -## Setup +- DisplayUtil/EspUtil: Converting images to two-color images and ESP compression +- DisplayUtil/HomeAssistant: utilities for working with HomeAssistant entities +- DisplayUtil/Layouting: Layouting system +- DisplayUtil/MqttExport: Export URL to MQTT +- DisplayUtil/Providers: various providers (for fonts & svg icons) +- DisplayUtil/Screens: Utilities for screen management (various outputs) +- DisplayUtil/Template: Utilities for handling Scriban templates and their rendering +- DisplayUtil/Utils: various utils +- DisplayUtil/XmlModel: Deserialization of XML content + +## Configuration + +This application requires many modules, with different configuration + +### HomeAssistant + +When enabling it mirrors the Hass States and exposes the following Scriban functions + +| Scriban Function | Description | +| ----------------------------------------------------------------- | ------------------------------- | +| `hass.get_state(entity_id: string): string` | Gets a state from HomeAssistant | +| `hass.get_attribute(entity_id: string, attribute:string): string` | Gets an attribute of a entity | +| `hass.get_float_state(entity_id: string): float` | Gets a state as float | +| `hass.get_datetime_state(entity_id: string): DateTime` | Gets a state as DateTime | + +To enable the Hass support the following configuration needs to be provided: + +| Field | Description | +| ---------------------------------------------- | ----------------------------- | +| **`HomeAssistant__Host`** | Host of the Hass instance | +| **`HomeAssistant__Token`** | Token for Hass WebSocket | +| `HomeAssistant__Port = 8123` | Port of WebSocket API | +| `HomeAssistant__Ssl = false` | Enables SSL for the WebSocket | +| `HomeAssistant__WebsocketPath = api/websocket` | Path to the WebSocket Api | + +### HomeAssistant Calendar Integration + +On top of Hass Integration, an additional handling for Calendars can be enabled. Therefor additional configuration is required. +Calendar Appointments are fetched once an hour and are available in Scriban with the `appointments` variable + +| Field | Description | +| ------------------------------------------------------ | ------------------------------------------------------ | +| `HomeAssistant__CalendarEntities__` | Calendar entities which are used to fetch appointments | From 3bd1f75a769255fe2c209e849b68dd409c3632f1 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Mon, 22 Apr 2024 18:38:24 +0200 Subject: [PATCH 11/23] Update NetDaemon --- DisplayUtil/DisplayUtil.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DisplayUtil/DisplayUtil.csproj b/DisplayUtil/DisplayUtil.csproj index 713dd93..f3c78c5 100644 --- a/DisplayUtil/DisplayUtil.csproj +++ b/DisplayUtil/DisplayUtil.csproj @@ -10,8 +10,8 @@ - - + + From b1de2c0a3ca799ff217a71786910abec4d7f5aac Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Mon, 22 Apr 2024 18:38:59 +0200 Subject: [PATCH 12/23] Use new public method to add ScopedHaContext --- DisplayUtil/HomeAssistant/HassExtensions.cs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/DisplayUtil/HomeAssistant/HassExtensions.cs b/DisplayUtil/HomeAssistant/HassExtensions.cs index 615db21..9b62c5b 100644 --- a/DisplayUtil/HomeAssistant/HassExtensions.cs +++ b/DisplayUtil/HomeAssistant/HassExtensions.cs @@ -21,17 +21,8 @@ public static IHostApplicationBuilder AddHassSupport(this IHostApplicationBuilde builder.Services .AddHomeAssistantClient() - .AddScoped(); - - // Hack: Initialize Hass Model - var extensionType = typeof(DependencyInjectionSetup); - var methodInfo = extensionType.GetMethod("AddScopedHaContext", - System.Reflection.BindingFlags.NonPublic - | System.Reflection.BindingFlags.Static - ) - ?? throw new Exception("Error injecting Hass Model"); - - methodInfo.Invoke(null, [builder.Services]); + .AddScoped() + .AddScopedHaContext(); // Background Connection builder.Services.AddHostedService(); From fc595b341fa64b2528567a2573c8b38873a36624 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Thu, 25 Apr 2024 15:29:20 +0200 Subject: [PATCH 13/23] Change Launch Config --- .vscode/launch.json | 14 ++++++++++---- .vscode/tasks.json | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 8df1f03..06a3906 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,11 +1,17 @@ { "configurations": [ { - "name": "C#: DisplayUtil [https]", - "type": "dotnet", + "name": "C#: DisplayUtil Server", + "type": "coreclr", "request": "launch", - "projectPath": "${workspaceFolder}\\DisplayUtil\\DisplayUtil.csproj", - "launchConfigurationId": "TargetFramework=;https", + "preLaunchTask": "build_webserver", + "program": "${workspaceFolder}/DisplayUtil/bin/Debug/net8.0/DisplayUtil.dll", + "args": [], + "cwd": "${workspaceFolder}/DisplayUtil", + "stopAtEntry": false, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, "logging": { "moduleLoad": false } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..0b6cee2 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build_webserver", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/DisplayUtil/DisplayUtil.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} From c77f80f2ecb289b60ac5803f02304a19a45a7ff3 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sat, 27 Apr 2024 17:11:44 +0200 Subject: [PATCH 14/23] Add Quartz Scheduler --- DisplayUtil/DisplayUtil.csproj | 1 + DisplayUtil/Program.cs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/DisplayUtil/DisplayUtil.csproj b/DisplayUtil/DisplayUtil.csproj index f3c78c5..555f852 100644 --- a/DisplayUtil/DisplayUtil.csproj +++ b/DisplayUtil/DisplayUtil.csproj @@ -12,6 +12,7 @@ + diff --git a/DisplayUtil/Program.cs b/DisplayUtil/Program.cs index fa97a33..5f58775 100644 --- a/DisplayUtil/Program.cs +++ b/DisplayUtil/Program.cs @@ -6,6 +6,7 @@ using DisplayUtil.Template; using DisplayUtil.Utils; using DisplayUtil.XmlModel; +using Quartz; var builder = WebApplication.CreateBuilder(args); builder.Configuration.AddJsonFile("appsettings.Local.json", true); @@ -23,7 +24,9 @@ builder.Services .AddSingleton() - .AddTransient(); + .AddTransient() + .AddQuartz() + .AddQuartzHostedService(); builder.Services.AddScreenProvider(o => o .AddScribanFiles() @@ -44,8 +47,7 @@ .WithName("Preview Image") .WithOpenApi(); -app.UseEspUtilities() - .UseMqttWriter(); +app.UseEspUtilities(); app.UseStaticFiles(); app.Run(); \ No newline at end of file From 643e226ec09897043e8bc9a32e999530c4bf9329 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sat, 27 Apr 2024 17:11:52 +0200 Subject: [PATCH 15/23] Rewrite exporting to function --- DisplayUtil/MqttExport/MqttExportJob.cs | 24 ------------ DisplayUtil/MqttExport/MqttInitExtension.cs | 27 +------------ DisplayUtil/MqttExport/MqttSettings.cs | 10 ----- .../MqttExport/MqttTemplateExtender.cs | 32 +++++++++++++++ DisplayUtil/MqttExport/MqttUrlRenderer.cs | 25 +++++------- DisplayUtil/Template/ITemplateExtender.cs | 7 +++- DisplayUtil/Template/Jobs/JobsExtension.cs | 28 +++++++++++++ DisplayUtil/Template/Jobs/TemplateJob.cs | 17 ++++++++ DisplayUtil/Template/TemplateInitializer.cs | 5 ++- DisplayUtil/Template/TemplateLoader.cs | 2 +- DisplayUtil/Template/TemplateRenderer.cs | 39 +++++++++++++++++-- DisplayUtil/Template/TemplateSettings.cs | 13 +++++++ 12 files changed, 148 insertions(+), 81 deletions(-) delete mode 100644 DisplayUtil/MqttExport/MqttExportJob.cs create mode 100644 DisplayUtil/MqttExport/MqttTemplateExtender.cs create mode 100644 DisplayUtil/Template/Jobs/JobsExtension.cs create mode 100644 DisplayUtil/Template/Jobs/TemplateJob.cs diff --git a/DisplayUtil/MqttExport/MqttExportJob.cs b/DisplayUtil/MqttExport/MqttExportJob.cs deleted file mode 100644 index 4f7f277..0000000 --- a/DisplayUtil/MqttExport/MqttExportJob.cs +++ /dev/null @@ -1,24 +0,0 @@ -using DisplayUtil.Utils; -using Microsoft.Extensions.Options; -using NetDaemon.HassModel; - -namespace DisplayUtil.MqttExport; - -/// -/// Job to publish the URI to MQTT -/// -public class MqttExportJob( - IServiceScopeFactory scopeFactory, - ILogger logger, - IOptions options -) : TimedScopedService(scopeFactory, logger) -{ - protected override TimeSpan InitTimeout => TimeSpan.FromSeconds(5); - protected override TimeSpan Delay => options.Value.RefreshInterval!.Value; - - protected override async Task TriggerAsync(IServiceProvider serviceProvider) - { - var renderer = serviceProvider.GetRequiredService(); - await renderer.RenderUrlAndPublish(); - } -} \ No newline at end of file diff --git a/DisplayUtil/MqttExport/MqttInitExtension.cs b/DisplayUtil/MqttExport/MqttInitExtension.cs index 4446e0c..2140ba3 100644 --- a/DisplayUtil/MqttExport/MqttInitExtension.cs +++ b/DisplayUtil/MqttExport/MqttInitExtension.cs @@ -1,3 +1,4 @@ +using DisplayUtil.Template; using DisplayUtil.Utils; using MQTTnet; using MQTTnet.Client; @@ -19,12 +20,7 @@ public static IHostApplicationBuilder AddMqttWriter(this IHostApplicationBuilder else builder.Services.AddSingleton(); - if ( - settings.ScreenDetectTemplate is null - || settings.RefreshInterval is null - ) return builder; - - builder.Services.AddHostedService(); + builder.Services.AddScoped(); return builder; } @@ -61,23 +57,4 @@ MqttSettings settings return true; } - - public static WebApplication UseMqttWriter(this WebApplication app) - { - if (app.Services.GetService() is null) return app; - - app.MapGet("/mqtt/uri", async (MqttUrlRenderer renderer) => - { - return Results.Ok(await renderer.GetMqttTemplateUriAsync()); - }) - .WithName("Get MQTT URI") - .WithOpenApi(); - - app.MapGet("/mqtt/template", async (MqttUrlRenderer renderer) - => Results.Ok(await renderer.GetMqttTemplateAsync())) - .WithName("Get MQTT Template") - .WithOpenApi(); - - return app; - } } diff --git a/DisplayUtil/MqttExport/MqttSettings.cs b/DisplayUtil/MqttExport/MqttSettings.cs index a68dfb3..0fd1963 100644 --- a/DisplayUtil/MqttExport/MqttSettings.cs +++ b/DisplayUtil/MqttExport/MqttSettings.cs @@ -25,16 +25,6 @@ public record MqttSettings /// public string? Topic { get; init; } - /// - /// Template used, to detect the MQTT Template - /// - public string? ScreenDetectTemplate { get; init; } - - /// - /// Interval for rerendering the Template - /// - public TimeSpan? RefreshInterval { get; init; } - /// /// Should the message only be updated, when the value changes? /// diff --git a/DisplayUtil/MqttExport/MqttTemplateExtender.cs b/DisplayUtil/MqttExport/MqttTemplateExtender.cs new file mode 100644 index 0000000..c4e509f --- /dev/null +++ b/DisplayUtil/MqttExport/MqttTemplateExtender.cs @@ -0,0 +1,32 @@ +using DisplayUtil.Template; +using Scriban.Runtime; + +namespace DisplayUtil.MqttExport; + +/// +/// Provides the publish_mqtt_template function to Jobs +/// +/// +public class MqttTemplateExtender( + MqttUrlRenderer setter +) : ITemplateExtender +{ + public void Enrich( + ScriptObject scriptObject, + EnrichScope scope) + { + if (scope != EnrichScope.Job) return; + + scriptObject.Import("publish_mqtt_template", SetMqttTemplate); + } + + private void SetMqttTemplate(string templateId) + { + _ = SetMqttTemplateAsync(templateId.Trim()); + } + + private async Task SetMqttTemplateAsync(string templateId) + { + await setter.GenerateUrlAndPublish(templateId); + } +} \ No newline at end of file diff --git a/DisplayUtil/MqttExport/MqttUrlRenderer.cs b/DisplayUtil/MqttExport/MqttUrlRenderer.cs index f9a6cbc..356476e 100644 --- a/DisplayUtil/MqttExport/MqttUrlRenderer.cs +++ b/DisplayUtil/MqttExport/MqttUrlRenderer.cs @@ -5,15 +5,14 @@ namespace DisplayUtil.MqttExport; public class MqttUrlRenderer( - TemplateRenderer renderer, MqttExporter exporter, IOptionsSnapshot options ) { - public async Task GetMqttTemplateUriAsync() + private Uri GetMqttTemplateUri( + string providerId + ) { - var providerId = await GetMqttTemplateAsync(); - var settings = options.Value; var query = providerId.IndexOf('?'); @@ -35,23 +34,17 @@ public async Task GetMqttTemplateUriAsync() return uriBuilder.Uri; } - public async Task GetMqttTemplateAsync() - { - var template = options.Value.ScreenDetectTemplate!; - - var result = await renderer.RenderAsync(template, - EnrichScope.TemplateProvider); - - return result.Trim(); - } - /// /// Exports the Uri to MQTT /// /// Task - public async Task RenderUrlAndPublish() + public async Task GenerateUrlAndPublish( + string providerId + ) { - await exporter.PublishUriToMqttAsync(await GetMqttTemplateUriAsync()); + await exporter.PublishUriToMqttAsync( + GetMqttTemplateUri(providerId) + ); } } \ No newline at end of file diff --git a/DisplayUtil/Template/ITemplateExtender.cs b/DisplayUtil/Template/ITemplateExtender.cs index 70d6f0e..6fdf2d8 100644 --- a/DisplayUtil/Template/ITemplateExtender.cs +++ b/DisplayUtil/Template/ITemplateExtender.cs @@ -16,7 +16,12 @@ public enum EnrichScope /// /// This template will used for Providing an other template /// - TemplateProvider + TemplateProvider, + + /// + /// This template will used as Job + /// + Job } /// diff --git a/DisplayUtil/Template/Jobs/JobsExtension.cs b/DisplayUtil/Template/Jobs/JobsExtension.cs new file mode 100644 index 0000000..3a5cf72 --- /dev/null +++ b/DisplayUtil/Template/Jobs/JobsExtension.cs @@ -0,0 +1,28 @@ +using Quartz; + +namespace DisplayUtil.Template.Jobs; + +internal static class JobsExtension +{ + public static void AddTemplateJobs( + this IHostApplicationBuilder builder, + TemplateSettings settings + ) + { + var jobConfigurations = settings.Jobs; + + builder.Services.AddQuartz(q => + { + foreach (var (key, setting) in jobConfigurations) + { + q.ScheduleJob(job => + { + job.WithIdentity(key) + .UsingJobData(TemplateJob.TemplateNameField, + setting.TemplateName) + .WithCronSchedule(setting.Cron); + }); + } + }); + } +} \ No newline at end of file diff --git a/DisplayUtil/Template/Jobs/TemplateJob.cs b/DisplayUtil/Template/Jobs/TemplateJob.cs new file mode 100644 index 0000000..856a6bd --- /dev/null +++ b/DisplayUtil/Template/Jobs/TemplateJob.cs @@ -0,0 +1,17 @@ +using Quartz; + +namespace DisplayUtil.Template.Jobs; + +public class TemplateJob(TemplateRenderer renderer) : IJob +{ + public const string TemplateNameField = "TemplateName"; + + public async Task Execute(IJobExecutionContext context) + { + var templateNameObject = context.Get(TemplateNameField); + if (templateNameObject is not string templateName) + throw new Exception("Unable to parse Template Name"); + + await renderer.EvaluateAsync(templateName); + } +} \ No newline at end of file diff --git a/DisplayUtil/Template/TemplateInitializer.cs b/DisplayUtil/Template/TemplateInitializer.cs index 6c783fd..b902df9 100644 --- a/DisplayUtil/Template/TemplateInitializer.cs +++ b/DisplayUtil/Template/TemplateInitializer.cs @@ -1,3 +1,4 @@ +using DisplayUtil.Template.Jobs; using DisplayUtil.Utils; namespace DisplayUtil.Template; @@ -6,7 +7,7 @@ public static class TemplateInitializer { public static IHostApplicationBuilder AddTemplates(this IHostApplicationBuilder builder) { - builder.ConfigureAndGet("Templates"); + var settings = builder.ConfigureAndGet("Templates"); builder.Services .AddSingleton() @@ -14,6 +15,8 @@ public static IHostApplicationBuilder AddTemplates(this IHostApplicationBuilder .AddScoped() .AddSingleton(); + builder.AddTemplateJobs(settings!); + return builder; } diff --git a/DisplayUtil/Template/TemplateLoader.cs b/DisplayUtil/Template/TemplateLoader.cs index e707414..b0d2a0c 100644 --- a/DisplayUtil/Template/TemplateLoader.cs +++ b/DisplayUtil/Template/TemplateLoader.cs @@ -9,7 +9,7 @@ namespace DisplayUtil.Template; public class TemplateLoader(IOptions options) : ITemplateLoader { private static readonly string[] _allowedExtensions = [ - "sbntxt", "sbnxml" + "sbntxt", "sbnxml", "sbn" ]; public string GetPath(string templateName) diff --git a/DisplayUtil/Template/TemplateRenderer.cs b/DisplayUtil/Template/TemplateRenderer.cs index 0ab50a6..746aa69 100644 --- a/DisplayUtil/Template/TemplateRenderer.cs +++ b/DisplayUtil/Template/TemplateRenderer.cs @@ -3,6 +3,7 @@ using DisplayUtil.MqttExport; using Microsoft.Extensions.Options; using Scriban; +using Scriban.Parsing; namespace DisplayUtil.Template; @@ -15,8 +16,9 @@ public class TemplateRenderer( TemplateLoader loader ) { - public async Task RenderAsync( - string templateName, EnrichScope scope + private async Task GetTemplateAsync( + string templateName, + LexerOptions lexerOptions ) { var path = Path.IsPathFullyQualified(templateName) ? @@ -24,7 +26,38 @@ public async Task RenderAsync( : loader.GetPath(templateName); var content = await loader.LoadAsync(path); - var template = Scriban.Template.Parse(content); + return Scriban.Template.Parse(content, templateName, null, + lexerOptions); + } + + /// + /// Evaluates a Scriban Script + /// + /// Name of the Template + /// Task + public async Task EvaluateAsync(string templateName) + { + var template = await GetTemplateAsync( + templateName, + new LexerOptions() { Mode = ScriptMode.ScriptOnly } + ); + var context = contextProvider.GetTemplateContext(EnrichScope.Job); + + await template.EvaluateAsync(context); + } + + /// + /// Renders a Scriban Template + /// + /// Name of the Template + /// EnrichScope + /// Result of the rendering + public async Task RenderAsync( + string templateName, EnrichScope scope + ) + { + var template = await GetTemplateAsync(templateName, + LexerOptions.Default); var context = contextProvider.GetTemplateContext(scope); return await template.RenderAsync(context); diff --git a/DisplayUtil/Template/TemplateSettings.cs b/DisplayUtil/Template/TemplateSettings.cs index 38b2810..0708d5f 100644 --- a/DisplayUtil/Template/TemplateSettings.cs +++ b/DisplayUtil/Template/TemplateSettings.cs @@ -10,4 +10,17 @@ public record TemplateSettings public IReadOnlyDictionary Paths { get; init; } = new Dictionary(); + /// + /// Defines background template jobs + /// Key = any unique string (key) + /// Value = definition for the Job + /// + public IReadOnlyDictionary Jobs { get; init; } + = new Dictionary(); +} + +public record TemplateJobConfiguration +{ + public string Cron { get; init; } = null!; + public string TemplateName { get; init; } = null!; } \ No newline at end of file From b6eb6e193fbc72a391b09bbf5323a2a73cc96972 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 15:16:03 +0200 Subject: [PATCH 16/23] Move HassCalendar to Quartz --- .../HomeAssistant/Calendar/HassCalendarJob.cs | 22 ------------------- .../Calendar/HassCalendarWorker.cs | 9 ++++++-- DisplayUtil/HomeAssistant/HassExtensions.cs | 16 ++++++++++++-- 3 files changed, 21 insertions(+), 26 deletions(-) delete mode 100644 DisplayUtil/HomeAssistant/Calendar/HassCalendarJob.cs diff --git a/DisplayUtil/HomeAssistant/Calendar/HassCalendarJob.cs b/DisplayUtil/HomeAssistant/Calendar/HassCalendarJob.cs deleted file mode 100644 index 91f17cd..0000000 --- a/DisplayUtil/HomeAssistant/Calendar/HassCalendarJob.cs +++ /dev/null @@ -1,22 +0,0 @@ -using DisplayUtil.Utils; - -namespace DisplayUtil.HomeAssistant.Calendar; - -/// -/// Job to sync HassAppointment -/// -internal class HassCalendarJob( - IServiceScopeFactory scopeFactory, - ILogger logger -) : TimedScopedService(scopeFactory, logger) -{ - protected override TimeSpan InitTimeout => TimeSpan.FromSeconds(5); - - protected override TimeSpan Delay => TimeSpan.FromHours(1); - - protected override async Task TriggerAsync(IServiceProvider serviceProvider) - { - var worker = serviceProvider.GetRequiredService(); - await worker.RefreshAsync(); - } -} diff --git a/DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs b/DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs index f4ac3dc..5f25554 100644 --- a/DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs +++ b/DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Options; using NetDaemon.Client; using NetDaemon.Client.HomeAssistant.Model; +using Quartz; namespace DisplayUtil.HomeAssistant.Calendar; @@ -10,7 +11,7 @@ public partial class HassCalendarWorker( HassAppointmentStore store, IOptions options, ILogger logger -) +) : IJob { private readonly ILogger _logger = logger; @@ -20,7 +21,7 @@ public async Task RefreshAsync() store.Appointments = appointments ?? []; } - public async Task FetchAsync(CancellationToken cancellation) + private async Task FetchAsync(CancellationToken cancellation) { LogFetch(); @@ -65,6 +66,10 @@ public async Task RefreshAsync() [LoggerMessage(LogLevel.Warning, "Error fetching appointments")] private partial void LogError(Exception e); + public Task Execute(IJobExecutionContext context) + { + return RefreshAsync(); + } } internal record GetEventsPayload : CommandMessage diff --git a/DisplayUtil/HomeAssistant/HassExtensions.cs b/DisplayUtil/HomeAssistant/HassExtensions.cs index 9b62c5b..96d8b3e 100644 --- a/DisplayUtil/HomeAssistant/HassExtensions.cs +++ b/DisplayUtil/HomeAssistant/HassExtensions.cs @@ -4,6 +4,7 @@ using NetDaemon.Client.Extensions; using NetDaemon.Client.Settings; using NetDaemon.HassModel; +using Quartz; namespace DisplayUtil.HomeAssistant; @@ -35,8 +36,19 @@ public static IHostApplicationBuilder AddHassSupport(this IHostApplicationBuilde builder.Services .AddSingleton() .AddSingleton(s => s.GetRequiredService()) - .AddScoped() - .AddHostedService(); + .AddScoped(); + + builder.Services.Configure(o => + { + var jobKey = new JobKey(nameof(HassCalendarWorker)); + o.AddJob(j => j.WithIdentity(jobKey)); + + o.AddTrigger(t => t + .ForJob(jobKey) + .StartAt(DateTime.Now + TimeSpan.FromSeconds(30)) + .WithSimpleSchedule(s => s.WithIntervalInHours(1)) + ); + }); return builder; } From 485b05e88ccfbf7064e6a9d59cb20057aea0c51e Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 15:17:08 +0200 Subject: [PATCH 17/23] Rename Job --- .../{HassCalendarWorker.cs => HassCalendarImportJob.cs} | 4 ++-- DisplayUtil/HomeAssistant/HassExtensions.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename DisplayUtil/HomeAssistant/Calendar/{HassCalendarWorker.cs => HassCalendarImportJob.cs} (97%) diff --git a/DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs b/DisplayUtil/HomeAssistant/Calendar/HassCalendarImportJob.cs similarity index 97% rename from DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs rename to DisplayUtil/HomeAssistant/Calendar/HassCalendarImportJob.cs index 5f25554..a5bc37c 100644 --- a/DisplayUtil/HomeAssistant/Calendar/HassCalendarWorker.cs +++ b/DisplayUtil/HomeAssistant/Calendar/HassCalendarImportJob.cs @@ -6,11 +6,11 @@ namespace DisplayUtil.HomeAssistant.Calendar; -public partial class HassCalendarWorker( +public partial class HassCalendarImportJob( IHomeAssistantConnection client, HassAppointmentStore store, IOptions options, - ILogger logger + ILogger logger ) : IJob { private readonly ILogger _logger = logger; diff --git a/DisplayUtil/HomeAssistant/HassExtensions.cs b/DisplayUtil/HomeAssistant/HassExtensions.cs index 96d8b3e..9bec15a 100644 --- a/DisplayUtil/HomeAssistant/HassExtensions.cs +++ b/DisplayUtil/HomeAssistant/HassExtensions.cs @@ -36,12 +36,12 @@ public static IHostApplicationBuilder AddHassSupport(this IHostApplicationBuilde builder.Services .AddSingleton() .AddSingleton(s => s.GetRequiredService()) - .AddScoped(); + .AddScoped(); builder.Services.Configure(o => { - var jobKey = new JobKey(nameof(HassCalendarWorker)); - o.AddJob(j => j.WithIdentity(jobKey)); + var jobKey = new JobKey(nameof(HassCalendarImportJob)); + o.AddJob(j => j.WithIdentity(jobKey)); o.AddTrigger(t => t .ForJob(jobKey) From 9aa9681ac14dbbac8fb408972087662a3b910e1a Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 15:24:52 +0200 Subject: [PATCH 18/23] Rewrite SecurityTimeout --- DisplayUtil/HomeAssistant/HassExtensions.cs | 2 +- DisplayUtil/Template/Jobs/JobsExtension.cs | 22 +++++++++++++------- DisplayUtil/Utils/TriggerBuilderExtension.cs | 17 +++++++++++++++ 3 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 DisplayUtil/Utils/TriggerBuilderExtension.cs diff --git a/DisplayUtil/HomeAssistant/HassExtensions.cs b/DisplayUtil/HomeAssistant/HassExtensions.cs index 9bec15a..1631c7c 100644 --- a/DisplayUtil/HomeAssistant/HassExtensions.cs +++ b/DisplayUtil/HomeAssistant/HassExtensions.cs @@ -45,7 +45,7 @@ public static IHostApplicationBuilder AddHassSupport(this IHostApplicationBuilde o.AddTrigger(t => t .ForJob(jobKey) - .StartAt(DateTime.Now + TimeSpan.FromSeconds(30)) + .WithSecurityTimeout() .WithSimpleSchedule(s => s.WithIntervalInHours(1)) ); }); diff --git a/DisplayUtil/Template/Jobs/JobsExtension.cs b/DisplayUtil/Template/Jobs/JobsExtension.cs index 3a5cf72..2ae841c 100644 --- a/DisplayUtil/Template/Jobs/JobsExtension.cs +++ b/DisplayUtil/Template/Jobs/JobsExtension.cs @@ -1,9 +1,12 @@ +using DisplayUtil.Utils; using Quartz; namespace DisplayUtil.Template.Jobs; internal static class JobsExtension { + private const string KeyGroup = nameof(TemplateJob); + public static void AddTemplateJobs( this IHostApplicationBuilder builder, TemplateSettings settings @@ -11,17 +14,22 @@ TemplateSettings settings { var jobConfigurations = settings.Jobs; - builder.Services.AddQuartz(q => + builder.Services.Configure(o => { foreach (var (key, setting) in jobConfigurations) { - q.ScheduleJob(job => - { - job.WithIdentity(key) - .UsingJobData(TemplateJob.TemplateNameField, + var jobKey = new JobKey(key, KeyGroup); + o.AddJob(j => j + .WithIdentity(jobKey) + .UsingJobData(TemplateJob.TemplateNameField, setting.TemplateName) - .WithCronSchedule(setting.Cron); - }); + ); + + o.AddTrigger(t => t + .WithCronSchedule(setting.Cron) + .WithSecurityTimeout() + .WithIdentity($"{key}_schedule", KeyGroup) + ); } }); } diff --git a/DisplayUtil/Utils/TriggerBuilderExtension.cs b/DisplayUtil/Utils/TriggerBuilderExtension.cs new file mode 100644 index 0000000..1b8fc75 --- /dev/null +++ b/DisplayUtil/Utils/TriggerBuilderExtension.cs @@ -0,0 +1,17 @@ +using Quartz; + +namespace DisplayUtil.Utils; + +public static class TriggerBuilderExtension +{ + + private static TimeSpan _securityTimeout = TimeSpan.FromSeconds(30); + + public static TriggerBuilder WithSecurityTimeout(this TriggerBuilder builder) + { + return builder.StartAt( + DateTimeOffset.Now + _securityTimeout + ); + } + +} \ No newline at end of file From a3603cd37b337bda02fcd91dcc33af0fda903719 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 15:33:25 +0200 Subject: [PATCH 19/23] Add Job Finalizer --- DisplayUtil/Program.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/DisplayUtil/Program.cs b/DisplayUtil/Program.cs index 5f58775..8716891 100644 --- a/DisplayUtil/Program.cs +++ b/DisplayUtil/Program.cs @@ -26,7 +26,10 @@ .AddSingleton() .AddTransient() .AddQuartz() - .AddQuartzHostedService(); + .AddQuartzHostedService(options => + { + options.WaitForJobsToComplete = true; + }); builder.Services.AddScreenProvider(o => o .AddScribanFiles() From d603d32b3b92ffaffcc3b184b806592e1c747d6d Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 15:33:33 +0200 Subject: [PATCH 20/23] Hide Quartz Logging --- DisplayUtil/appsettings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DisplayUtil/appsettings.json b/DisplayUtil/appsettings.json index 10f68b8..f7e7b1f 100644 --- a/DisplayUtil/appsettings.json +++ b/DisplayUtil/appsettings.json @@ -2,8 +2,9 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Microsoft.AspNetCore": "Warning", + "Quartz": "Warning" } }, "AllowedHosts": "*" -} +} \ No newline at end of file From 43e38da78110ac97ebe88a619e39e76e56ba5cfa Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 15:33:42 +0200 Subject: [PATCH 21/23] Add missed Job --- DisplayUtil/Template/Jobs/JobsExtension.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/DisplayUtil/Template/Jobs/JobsExtension.cs b/DisplayUtil/Template/Jobs/JobsExtension.cs index 2ae841c..45c09b7 100644 --- a/DisplayUtil/Template/Jobs/JobsExtension.cs +++ b/DisplayUtil/Template/Jobs/JobsExtension.cs @@ -26,6 +26,7 @@ TemplateSettings settings ); o.AddTrigger(t => t + .ForJob(jobKey) .WithCronSchedule(setting.Cron) .WithSecurityTimeout() .WithIdentity($"{key}_schedule", KeyGroup) From 1c32f6cea4bdf33fae44498dca0a3f0dcc4d16b9 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 15:33:52 +0200 Subject: [PATCH 22/23] Fix getting value --- DisplayUtil/Template/Jobs/TemplateJob.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DisplayUtil/Template/Jobs/TemplateJob.cs b/DisplayUtil/Template/Jobs/TemplateJob.cs index 856a6bd..566b479 100644 --- a/DisplayUtil/Template/Jobs/TemplateJob.cs +++ b/DisplayUtil/Template/Jobs/TemplateJob.cs @@ -8,7 +8,7 @@ public class TemplateJob(TemplateRenderer renderer) : IJob public async Task Execute(IJobExecutionContext context) { - var templateNameObject = context.Get(TemplateNameField); + var templateNameObject = context.MergedJobDataMap.Get(TemplateNameField); if (templateNameObject is not string templateName) throw new Exception("Unable to parse Template Name"); From c1001ce1a45154ef981667af296d87aa57f8f109 Mon Sep 17 00:00:00 2001 From: Tim Ittermann Date: Sun, 28 Apr 2024 16:17:59 +0200 Subject: [PATCH 23/23] Fix float handling --- DisplayUtil/Template/UtilTemplateExtender.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/DisplayUtil/Template/UtilTemplateExtender.cs b/DisplayUtil/Template/UtilTemplateExtender.cs index a665bb5..2eb2559 100644 --- a/DisplayUtil/Template/UtilTemplateExtender.cs +++ b/DisplayUtil/Template/UtilTemplateExtender.cs @@ -18,7 +18,11 @@ public void Enrich(ScriptObject scriptObject, EnrichScope scope) public static float ToFloat(string? content) { if (content == null) return 0; - return float.Parse(content, CultureInfo.InvariantCulture); + if (float.TryParse(content, CultureInfo.InvariantCulture, out var value)) + { + return value; + } + return 0; } public static string TimespanToString(TimeSpan timeSpan, string format)