diff --git a/HomeAssistant/HassTemplateExtender.cs b/HomeAssistant/HassTemplateExtender.cs
index 0c1027b..bc6b143 100644
--- a/HomeAssistant/HassTemplateExtender.cs
+++ b/HomeAssistant/HassTemplateExtender.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
using DisplayUtil.Template;
using NetDaemon.HassModel;
using Scriban.Runtime;
@@ -14,6 +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);
context.Add("hass", hassObject);
}
@@ -39,8 +41,21 @@ private float GetFloatState(string entityId)
{
var state = GetState(entityId);
if (state == null) return 0f;
- return TemplateContextProvider.ToFloat(state);
+ return UtilTemplateExtender.ToFloat(state);
}
+ private DateTime? GetDateTime(string entityId)
+ {
+ var state = GetState(entityId);
+ if (state is null) return null;
+ if (!DateTime.TryParseExact(state,
+ "yyyy-MM-dd HH:mm:ss",
+ CultureInfo.GetCultureInfo("de-DE"),
+ DateTimeStyles.None,
+ out var dt))
+ return null;
+
+ return dt;
+ }
}
\ No newline at end of file
diff --git a/Layouting/ElementCollection.cs b/Layouting/ElementCollection.cs
index b2790e4..ac4c1ce 100644
--- a/Layouting/ElementCollection.cs
+++ b/Layouting/ElementCollection.cs
@@ -46,6 +46,16 @@ public ElementCollection Append(Element element)
return this;
}
+ ///
+ /// Add all Elements to this Collection
+ ///
+ /// The elements
+ public ElementCollection Append(IEnumerable elements)
+ {
+ Children.AddRange(elements);
+ return this;
+ }
+
protected override SKSize CalculateSize(DrawContext drawContext)
{
var childContext = BuildChildDrawContext(drawContext);
diff --git a/MqttExport/MqttExportJob.cs b/MqttExport/MqttExportJob.cs
index 4ff73e8..7f3284d 100644
--- a/MqttExport/MqttExportJob.cs
+++ b/MqttExport/MqttExportJob.cs
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Options;
+using NetDaemon.HassModel;
namespace DisplayUtil.MqttExport;
@@ -7,10 +8,13 @@ public class MqttExportJob(
IOptions optionsSnapshot
) : IHostedService
{
+ private static readonly TimeSpan InitTimeout = TimeSpan.FromSeconds(5);
private CancellationTokenSource? _cancellationTokenSource;
private async Task RunAsync(CancellationToken cancellation)
{
+ await Task.Delay(InitTimeout);
+
while (!cancellation.IsCancellationRequested)
{
await using (var scope = scopeFactory.CreateAsyncScope())
diff --git a/MqttExport/MqttExporter.cs b/MqttExport/MqttExporter.cs
index 1131868..dbb5795 100644
--- a/MqttExport/MqttExporter.cs
+++ b/MqttExport/MqttExporter.cs
@@ -1,9 +1,3 @@
-using System.Diagnostics;
-using DisplayUtil.EspUtilities;
-using DisplayUtil.Scenes;
-using Microsoft.Extensions.Options;
-using MQTTnet.Client;
-
namespace DisplayUtil.MqttExport;
///
@@ -11,37 +5,10 @@ namespace DisplayUtil.MqttExport;
/// Scoped
///
public class MqttExporter(
- ExportingMqttClient exportingMqttClient,
- IOptions options
+ ExportingMqttClient exportingMqttClient
)
{
- public Task ExportUriToMqtt(string providerId)
- {
- var settings = options.Value;
-
- var query = providerId.IndexOf('?');
- var providerPath = query == -1
- ? providerId
- : providerId[0..(query - 1)];
-
- var uriBuilder = new UriBuilder
- {
- Port = 80,
- Scheme = "http",
- Host = settings.ServerHostName,
- Path = EspUtilitiesInitExtension.CompressedImageRoute
- };
-
- uriBuilder.Path = EspUtilitiesInitExtension.CompressedImageRoute
- .Replace("{providerId}", providerPath);
-
- if (query != -1)
- uriBuilder.Query = providerId[query..];
-
- return SubmitAsync(uriBuilder.Uri);
- }
-
- public virtual async Task SubmitAsync(Uri uri)
+ public virtual async Task PublishUriToMqttAsync(Uri uri)
{
await exportingMqttClient.SendAsync(uri.ToString());
}
@@ -49,15 +16,14 @@ public virtual async Task SubmitAsync(Uri uri)
internal partial class CachedMqttExporter(
ExportingMqttClient exportingMqttClient,
- IOptions options,
ILogger logger)
- : MqttExporter(exportingMqttClient, options)
+ : MqttExporter(exportingMqttClient)
{
private readonly ILogger _logger = logger;
private Uri? _lastSubmission;
- public override Task SubmitAsync(Uri uri)
+ public override Task PublishUriToMqttAsync(Uri uri)
{
if (_lastSubmission == uri)
{
@@ -67,7 +33,7 @@ public override Task SubmitAsync(Uri uri)
LogSubmitting(uri);
_lastSubmission = uri;
- return base.SubmitAsync(uri);
+ return base.PublishUriToMqttAsync(uri);
}
[LoggerMessage(LogLevel.Debug, "Skipping resubmission of {uri}")]
diff --git a/MqttExport/MqttInitExtension.cs b/MqttExport/MqttInitExtension.cs
index 74378dd..4446e0c 100644
--- a/MqttExport/MqttInitExtension.cs
+++ b/MqttExport/MqttInitExtension.cs
@@ -61,4 +61,23 @@ 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/MqttExport/MqttUrlRenderer.cs b/MqttExport/MqttUrlRenderer.cs
index 7a7e76a..fac1fe1 100644
--- a/MqttExport/MqttUrlRenderer.cs
+++ b/MqttExport/MqttUrlRenderer.cs
@@ -1,3 +1,4 @@
+using DisplayUtil.EspUtilities;
using DisplayUtil.Template;
using Microsoft.Extensions.Options;
@@ -9,19 +10,51 @@ public class MqttUrlRenderer(
IOptionsSnapshot options
)
{
+ public async Task GetMqttTemplateUriAsync()
+ {
+ var providerId = await GetMqttTemplateAsync();
- ///
- /// Exports the Uri to MQTT
- ///
- /// Task
- public async Task RenderUrlAndPublish()
+ var settings = options.Value;
+
+ var query = providerId.IndexOf('?');
+ var providerPath = query == -1
+ ? providerId
+ : providerId[0..query];
+
+ var uriBuilder = new UriBuilder
+ {
+ Port = 80,
+ Scheme = "http",
+ Host = settings.ServerHostName,
+ Path = EspUtilitiesInitExtension.CompressedImageRoute
+ };
+
+ uriBuilder.Path = EspUtilitiesInitExtension.CompressedImageRoute
+ .Replace("{providerId}", providerPath);
+
+ if (query != -1)
+ uriBuilder.Query = providerId[query..];
+
+ return uriBuilder.Uri;
+ }
+
+ public async Task GetMqttTemplateAsync()
{
var template = options.Value.ScreenDetectTemplate!;
var result = await renderer.RenderAsync(template,
EnrichScope.TemplateProvider);
- await exporter.ExportUriToMqtt(result);
+ return result.Trim();
+ }
+
+ ///
+ /// Exports the Uri to MQTT
+ ///
+ /// Task
+ public async Task RenderUrlAndPublish()
+ {
+ await exporter.PublishUriToMqttAsync(await GetMqttTemplateUriAsync());
}
}
\ No newline at end of file
diff --git a/Program.cs b/Program.cs
index 3561b68..64bf8b7 100644
--- a/Program.cs
+++ b/Program.cs
@@ -25,7 +25,8 @@
.AddSingleton();
builder.Services.AddScoped()
- .AddScoped();
+ .AddScoped()
+ .AddSingleton();
builder.Services.AddTransient();
@@ -48,7 +49,8 @@
.WithName("Preview Image")
.WithOpenApi();
-app.UseEspUtilities();
+app.UseEspUtilities()
+ .UseMqttWriter();
app.UseStaticFiles();
app.Run();
\ No newline at end of file
diff --git a/Resources/screens/_icon_data.sbntxt b/Resources/screens/_icon_data.sbntxt
index 4263654..5a91a96 100644
--- a/Resources/screens/_icon_data.sbntxt
+++ b/Resources/screens/_icon_data.sbntxt
@@ -1,8 +1,8 @@
{{ func icon_data(sIconName, sText) }}
-{{ if (sText | string.strip | string.size ) > 0}}
-
-
-
-
-{{ end }}
+ {{ if (sText | string.strip | string.size ) > 0}}
+
+
+
+
+ {{ end }}
{{ end }}
\ No newline at end of file
diff --git a/Resources/screens/_media_player.sbntxt b/Resources/screens/_media_player.sbntxt
index 0e55eef..a196baa 100644
--- a/Resources/screens/_media_player.sbntxt
+++ b/Resources/screens/_media_player.sbntxt
@@ -26,14 +26,15 @@
{{ if state == "playing"
media_position = hass.get_attribute sPlayer "media_position" | to_float
media_duration = hass.get_attribute sPlayer "media_duration" | to_float
+ media_duration_ts = timespan.from_seconds media_duration
media_updated = hass.get_attribute sPlayer "media_position_updated_at" | date.parse
media_start = media_updated | date.add_seconds (-media_position)
media_end = media_start | date.add_seconds media_duration
}}
-
-
+ {{ icon_data "timer" (timespan.to_string media_duration_ts "c") }}
+ {{ icon_data "clock" (media_end | date.to_string "%T") }}
{{ else if state == "paused"
icon_data "pause" " -> Pausiert <- "
diff --git a/Resources/screens/_timer.sbntxt b/Resources/screens/_timer.sbntxt
new file mode 100644
index 0000000..168ae36
--- /dev/null
+++ b/Resources/screens/_timer.sbntxt
@@ -0,0 +1,20 @@
+{{ func timer() }}
+ {{
+ start_time = hass.get_datetime "input_datetime.googletimerstart"
+ end_time = hass.get_datetime "input_datetime.googletimerover"
+ timer_text = hass.get_state "input_text.countdown_text"
+ }}
+
+ {{ if start_time.year < 2000
+ ret 0
+ end}}
+
+
+
+ {{ icon_data "hourglass-start" (start_time | date.to_string "%T") }}
+ {{ icon_data "hourglass-end" (end_time | date.to_string "%T") }}
+
+
+ {{ ret 1 }}
+
+{{ end }}
\ No newline at end of file
diff --git a/Resources/screens/export_screen.sbntxt b/Resources/screens/export_screen.sbntxt
new file mode 100644
index 0000000..d1caca0
--- /dev/null
+++ b/Resources/screens/export_screen.sbntxt
@@ -0,0 +1,8 @@
+{{
+ state = hass.get_state "sensor.wohnzimmer_display_mode"
+}}
+{{ if state == "Normal" }}
+main?version={{ date.to_string date.now "%s" }}
+{{ else }}
+{{ state | string.downcase }}
+{{ end }}
diff --git a/Resources/screens/idle.sbntxt b/Resources/screens/idle.sbntxt
new file mode 100644
index 0000000..1a40969
--- /dev/null
+++ b/Resources/screens/idle.sbntxt
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Resources/screens/main.sbntxt b/Resources/screens/main.sbntxt
index c12e93d..00ca714 100644
--- a/Resources/screens/main.sbntxt
+++ b/Resources/screens/main.sbntxt
@@ -1,6 +1,9 @@
{{
include "_media_player"
include "_icon_data"
+ include "_timer"
+
+ items = 0
media_players = [
"media_player.buro",
@@ -31,19 +34,22 @@
-
+
{{ icon_data "couch" (hass.get_float_state "sensor.smart_hygrometer_0998_temperature" | math.round 2) + " °C"}}
{{ icon_data "cloud-sun" (hass.get_float_state "sensor.hue_outdoor_motion_sensor_1_temperature" | math.round 2) + " °C"}}
+
+ {{ icon_data "dumbbell" (hass.get_state "sensor.fitx_auslastung") + "%" }}
+
+
{{ for player in media_players
- media_player player
- end
- }}
+ items += media_player player
+ end }}
-
+ {{ items += timer }}
diff --git a/Resources/screens/night.sbntxt b/Resources/screens/night.sbntxt
new file mode 100644
index 0000000..ffb505d
--- /dev/null
+++ b/Resources/screens/night.sbntxt
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Template/TemplateContextProvider.cs b/Template/TemplateContextProvider.cs
index f46d010..8207c45 100644
--- a/Template/TemplateContextProvider.cs
+++ b/Template/TemplateContextProvider.cs
@@ -15,7 +15,6 @@ public class TemplateContextProvider(
public TemplateContext GetTemplateContext(EnrichScope scope)
{
var scriptObject = new ScriptObject();
- scriptObject.Import("to_float", ToFloat);
foreach (var extender in extenders)
{
@@ -32,10 +31,4 @@ public TemplateContext GetTemplateContext(EnrichScope scope)
return context;
}
-
- public static float ToFloat(string? content)
- {
- if (content == null) return 0;
- return float.Parse(content, CultureInfo.InvariantCulture);
- }
}
diff --git a/Template/UtilTemplateExtender.cs b/Template/UtilTemplateExtender.cs
new file mode 100644
index 0000000..a665bb5
--- /dev/null
+++ b/Template/UtilTemplateExtender.cs
@@ -0,0 +1,28 @@
+using System.Globalization;
+using Scriban.Runtime;
+
+namespace DisplayUtil.Template;
+
+internal class UtilTemplateExtender : ITemplateExtender
+{
+ public void Enrich(ScriptObject scriptObject, EnrichScope scope)
+ {
+ scriptObject.Import("to_float", ToFloat);
+
+ var timespanObject = new ScriptObject();
+ timespanObject.Import(typeof(TimeSpan));
+ timespanObject.Import("to_string", TimespanToString);
+ scriptObject.Add("timespan", timespanObject);
+ }
+
+ public static float ToFloat(string? content)
+ {
+ if (content == null) return 0;
+ return float.Parse(content, CultureInfo.InvariantCulture);
+ }
+
+ public static string TimespanToString(TimeSpan timeSpan, string format)
+ {
+ return timeSpan.ToString(format);
+ }
+}
\ No newline at end of file
diff --git a/XmlModel/Models/IXmlModel.cs b/XmlModel/Models/IXmlModel.cs
index 486f5a0..0efd17f 100644
--- a/XmlModel/Models/IXmlModel.cs
+++ b/XmlModel/Models/IXmlModel.cs
@@ -37,10 +37,9 @@ protected virtual ElementCollection FillWithChildren(
FaIconDrawer iconDrawer, FontProvider fontProvider, DefaultDefinition defaults
)
{
- foreach (var child in Children)
- {
- collection.Append(child.AsElement(iconDrawer, fontProvider, defaults));
- }
+ collection.Append(
+ Children.Select(e => e.AsElement(iconDrawer, fontProvider, defaults))
+ );
return collection;
}
}
\ No newline at end of file