Commit:
f03cb07Parent:
af79f96Add inside temperature to `WeatherPanel`
.containerfile
+2
-0
diff --git a/.containerfile b/.containerfile
index 17ab037..655ada8 100644
@@ -7,6 +7,7 @@ COPY ./MMirror.slnx ./global.json ./dotnet-tools.json ./Directory.Packages.props
COPY ./src/Integrations/Vasttrafik/Vasttrafik.csproj ./src/Integrations/Vasttrafik/packages.lock.json ./src/Integrations/Vasttrafik/
COPY ./src/Integrations/FaceCamera/FaceCamera.csproj ./src/Integrations/FaceCamera/packages.lock.json ./src/Integrations/FaceCamera/
COPY ./src/Integrations/Smhi/Smhi.csproj ./src/Integrations/Smhi/packages.lock.json ./src/Integrations/Smhi/
COPY ./src/Integrations/HomeAssistant/HomeAssistant.csproj ./src/Integrations/HomeAssistant/packages.lock.json ./src/Integrations/HomeAssistant/
COPY ./src/App/App.csproj ./src/App/packages.lock.json ./src/App/
RUN dotnet restore --locked-mode ./src/App/App.csproj
@@ -14,6 +15,7 @@ RUN dotnet restore --locked-mode ./src/App/App.csproj
COPY ./src/Integrations/Vasttrafik/ ./src/Integrations/Vasttrafik/
COPY ./src/Integrations/FaceCamera/ ./src/Integrations/FaceCamera/
COPY ./src/Integrations/Smhi/ ./src/Integrations/Smhi/
COPY ./src/Integrations/HomeAssistant/ ./src/Integrations/HomeAssistant/
COPY ./src/App/ ./src/App/
ARG SixLaborsLicenseKey
MMirror.slnx
+2
-0
diff --git a/MMirror.slnx b/MMirror.slnx
index 5c78015..4005cac 100644
@@ -4,11 +4,13 @@
</Folder>
<Folder Name="/src/Integrations/">
<Project Path="src/Integrations/FaceCamera/FaceCamera.csproj" />
<Project Path="src/Integrations/HomeAssistant/HomeAssistant.csproj" />
<Project Path="src/Integrations/Smhi/Smhi.csproj" />
<Project Path="src/Integrations/Vasttrafik/Vasttrafik.csproj" />
</Folder>
<Folder Name="/tests/Integrations/">
<Project Path="tests/Integrations/FaceCamera/FaceCamera.csproj" />
<Project Path="tests/Integrations/HomeAssistant/HomeAssistant.csproj" />
<Project Path="tests/Integrations/Smhi/Smhi.csproj" />
<Project Path="tests/Integrations/Vasttrafik/Vasttrafik.csproj" />
</Folder>
src/App/App.axaml.cs
+8
-1
diff --git a/src/App/App.axaml.cs b/src/App/App.axaml.cs
index b6109de..5cd0e8f 100644
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Hosting;
using MMirror.App.ViewModels;
using MMirror.App.Views;
using MMirror.Integrations.FaceCamera;
using MMirror.Integrations.HomeAssistant;
using MMirror.Integrations.Smhi;
using MMirror.Integrations.Vasttrafik;
@@ -27,7 +28,13 @@ public partial class App : Application
#if DEBUG
builder.Configuration.AddUserSecrets<App>();
#endif
builder.Services.AddViews().AddViewModels().AddSmhiServices().AddVasttrafikServices().AddFaceCameraServices();
builder
.Services.AddViews()
.AddViewModels()
.AddSmhiServices()
.AddHomeAssistantServices()
.AddVasttrafikServices()
.AddFaceCameraServices();
host = builder.Build();
src/App/App.csproj
+1
-0
diff --git a/src/App/App.csproj b/src/App/App.csproj
index b0526a5..90f5270 100644
@@ -13,6 +13,7 @@
<ProjectReference Include="..\Integrations\Smhi\Smhi.csproj" />
<ProjectReference Include="..\Integrations\Vasttrafik\Vasttrafik.csproj" />
<ProjectReference Include="..\Integrations\FaceCamera\FaceCamera.csproj" />
<ProjectReference Include="..\Integrations\HomeAssistant\HomeAssistant.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" />
src/App/ViewLocator.cs
+1
-1
diff --git a/src/App/ViewLocator.cs b/src/App/ViewLocator.cs
index 0ce0899..d31ee1a 100644
@@ -20,7 +20,7 @@ public class ViewLocator(IServiceProvider services) : IDataTemplate
MainViewModel => services.GetControlOrDefault<MainView>(),
DateTimePanelViewModel => services.GetControlOrDefault<DateTimePanel>(),
VasttrafikPanelViewModel => services.GetControlOrDefault<VasttrafikPanel>(),
SmhiPanelViewModel => services.GetControlOrDefault<SmhiPanel>(),
WeatherPanelViewModel => services.GetControlOrDefault<WeatherPanel>(),
_ => new TextBlock { Text = $"No view for {data?.GetType().Name}" },
};
src/App/ViewModels/MainViewModel.cs
+1
-1
diff --git a/src/App/ViewModels/MainViewModel.cs b/src/App/ViewModels/MainViewModel.cs
index f090bf7..6ee7365 100644
@@ -23,7 +23,7 @@ public partial class MainViewModel : ViewModelBase
FaceDetectionService faceDetectionService,
VasttrafikPanelViewModel olskrokstorget,
VasttrafikPanelViewModel svingeln,
SmhiPanelViewModel smhi,
WeatherPanelViewModel smhi,
DateTimePanelViewModel dateTimePanelViewModel
)
{
src/App/ViewModels/Panels/ServiceCollectionExtensions.cs
+1
-1
diff --git a/src/App/ViewModels/Panels/ServiceCollectionExtensions.cs b/src/App/ViewModels/Panels/ServiceCollectionExtensions.cs
index 036b2a6..1c3a204 100644
@@ -7,6 +7,6 @@ internal static class ServiceCollectionExtensions
extension(IServiceCollection services)
{
public IServiceCollection AddPanelViewModels() =>
services.AddDateTimePanelViewModel().AddSmhiPanelViewModel().AddVasttrafikPanelViewModel();
services.AddDateTimePanelViewModel().AddWeatherPanelViewModel().AddVasttrafikPanelViewModel();
}
}
src/App/ViewModels/Panels/WeatherPanelViewModel.cs
+31
-5
diff --git a/src/App/ViewModels/Panels/SmhiPanelViewModel.cs b/src/App/ViewModels/Panels/WeatherPanelViewModel.cs
similarity index 67%
rename from src/App/ViewModels/Panels/SmhiPanelViewModel.cs
rename to src/App/ViewModels/Panels/WeatherPanelViewModel.cs
index 50af7ca..6607924 100644
@@ -3,14 +3,20 @@ using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using MMirror.Integrations.HomeAssistant;
using MMirror.Integrations.Smhi;
namespace MMirror.App.ViewModels.Panels;
public partial class SmhiPanelViewModel(TimeProvider timeProvider, SmhiClient smhiClient) : ViewModelBase
public partial class WeatherPanelViewModel(
TimeProvider timeProvider,
SmhiClient smhiClient,
HomeAssistantClient homeAssistantClient
) : ViewModelBase
{
private readonly TimeProvider timeProvider = timeProvider;
private readonly SmhiClient smhiClient = smhiClient;
private readonly HomeAssistantClient homeAssistantClient = homeAssistantClient;
private CancellationTokenSource cts = new();
@@ -32,7 +38,27 @@ public partial class SmhiPanelViewModel(TimeProvider timeProvider, SmhiClient sm
{
while (!cancellationToken.IsCancellationRequested)
{
Forecast = await smhiClient.GetPointForecast(Longitude, Latitude, cancellationToken);
var newForecastTask = smhiClient.GetPointForecast(Longitude, Latitude, cancellationToken);
try
{
var localTemperature = await homeAssistantClient.GetTemperatures(cancellationToken);
var newForecast = await newForecastTask;
Forecast = newForecast with
{
Now = newForecast.Now with
{
Forecast = newForecast.Now.Forecast with
{
TemperatureCelsius = localTemperature.OutsideCelsius,
InsideTemperatureCelsius = localTemperature.InsideCelsius,
},
},
};
}
catch
{
Forecast = await newForecastTask;
}
await Task.Delay(TimeSpan.FromMinutes(15), timeProvider, cancellationToken);
}
@@ -45,13 +71,13 @@ public partial class SmhiPanelViewModel(TimeProvider timeProvider, SmhiClient sm
}
}
internal static class SmhiPanelViewModelServiceCollectionExtensions
internal static class WeatherPanelViewModelServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddSmhiPanelViewModel()
public IServiceCollection AddWeatherPanelViewModel()
{
services.AddTransient<SmhiPanelViewModel>();
services.AddTransient<WeatherPanelViewModel>();
return services;
}
}
src/App/Views/Panels/ServiceCollectionExtensions.cs
+1
-1
diff --git a/src/App/Views/Panels/ServiceCollectionExtensions.cs b/src/App/Views/Panels/ServiceCollectionExtensions.cs
index b1cddd1..53826a3 100644
@@ -7,6 +7,6 @@ internal static class ServiceCollectionExtensions
extension(IServiceCollection services)
{
public IServiceCollection AddPanels() =>
services.AddTransient<DateTimePanel>().AddTransient<SmhiPanel>().AddTransient<VasttrafikPanel>();
services.AddTransient<DateTimePanel>().AddTransient<WeatherPanel>().AddTransient<VasttrafikPanel>();
}
}
src/App/Views/Panels/WeatherPanel.axaml
+9
-4
diff --git a/src/App/Views/Panels/SmhiPanel.axaml b/src/App/Views/Panels/WeatherPanel.axaml
similarity index 87%
rename from src/App/Views/Panels/SmhiPanel.axaml
rename to src/App/Views/Panels/WeatherPanel.axaml
index 3a3f785..2281c23 100644
@@ -7,10 +7,10 @@
mc:Ignorable="d"
d:DesignWidth="720"
d:DesignHeight="1280"
x:Class="MMirror.App.Views.Panels.SmhiPanel"
x:DataType="vm:SmhiPanelViewModel">
x:Class="MMirror.App.Views.Panels.WeatherPanel"
x:DataType="vm:WeatherPanelViewModel">
<Design.DataContext>
<vm:SmhiPanelViewModel/>
<vm:WeatherPanelViewModel/>
</Design.DataContext>
<UserControl.Resources>
<x:Double x:Key="spacing">4</x:Double>
@@ -42,7 +42,12 @@
<Svg Path="{Binding Forecast.Now.Forecast.Symbol, Converter={StaticResource weatherSymbolIconConverter}}"
Width="128" Height="128" Grid.Column="0" />
<StackPanel Grid.Column="1" Spacing="{StaticResource spacing}" VerticalAlignment="Center">
<TextBlock Classes="h1 number" Text="{Binding Forecast.Now.Forecast.TemperatureCelsius, StringFormat={}{0:0} °C}" />
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto" ColumnSpacing="{StaticResource spacing}">
<TextBlock Classes="h1 number" Grid.Row="0" Grid.Column="0" Text="{Binding Forecast.Now.Forecast.TemperatureCelsius, StringFormat={}{0:0} °C}" TextAlignment="End" />
<TextBlock Grid.Row="0" Grid.Column="1" VerticalAlignment="Bottom">Ute</TextBlock>
<TextBlock Classes="h2 number" Grid.Row="1" Grid.Column="0" Text="{Binding Forecast.Now.Forecast.InsideTemperatureCelsius, StringFormat={}{0:0} °C}" TextAlignment="End" />
<TextBlock Grid.Row="1" Grid.Column="1" VerticalAlignment="Bottom">Inne</TextBlock>
</Grid>
<TextBlock Classes="h2 number" Text="{Binding Forecast.Now.Forecast.PrecipitationMillimeters, StringFormat={}{0:0.0} mm}" />
<TextBlock Classes="label" Text="Nu" />
</StackPanel>
src/App/Views/Panels/WeatherPanel.axaml.cs
+4
-4
diff --git a/src/App/Views/Panels/SmhiPanel.axaml.cs b/src/App/Views/Panels/WeatherPanel.axaml.cs
similarity index 90%
rename from src/App/Views/Panels/SmhiPanel.axaml.cs
rename to src/App/Views/Panels/WeatherPanel.axaml.cs
index 8b8ccf0..a1d3b76 100644
@@ -8,9 +8,9 @@ using MMirror.Integrations.Smhi;
namespace MMirror.App.Views.Panels;
public partial class SmhiPanel : UserControl
public partial class WeatherPanel : UserControl
{
public SmhiPanel()
public WeatherPanel()
{
InitializeComponent();
}
@@ -19,7 +19,7 @@ public partial class SmhiPanel : UserControl
{
base.OnAttachedToVisualTree(e);
if (DataContext is SmhiPanelViewModel vm)
if (DataContext is WeatherPanelViewModel vm)
{
vm.SubscribeToUpdates();
}
@@ -27,7 +27,7 @@ public partial class SmhiPanel : UserControl
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
if (DataContext is SmhiPanelViewModel vm)
if (DataContext is WeatherPanelViewModel vm)
{
vm.UnsubscribeFromUpdates();
}
src/App/packages.lock.json
+8
-0
diff --git a/src/App/packages.lock.json b/src/App/packages.lock.json
index 8f455df..9760915 100644
@@ -639,6 +639,14 @@
"SixLabors.ImageSharp": "[4.0.0, )"
}
},
"MMirror.Integrations.HomeAssistant": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Http": "[11.0.0-preview.4.26230.115, )",
"Microsoft.Extensions.Http.Resilience": "[10.6.0, )",
"Microsoft.Extensions.Options.ConfigurationExtensions": "[11.0.0-preview.4.26230.115, )"
}
},
"MMirror.Integrations.Smhi": {
"type": "Project",
"dependencies": {
src/Integrations/HomeAssistant/Dtos.cs
+11
-0
diff --git a/src/Integrations/HomeAssistant/Dtos.cs b/src/Integrations/HomeAssistant/Dtos.cs
new file mode 100644
index 0000000..f16a94b
@@ -0,0 +1,11 @@
using System.Text.Json.Serialization;
namespace MMirror.Integrations.HomeAssistant;
[JsonSerializable(typeof(EntityState[]))]
internal sealed partial class HomeAssistantJsonSerializerContext : JsonSerializerContext;
internal sealed record EntityState(
[property: JsonPropertyName("entity_id")] string EntityId,
[property: JsonPropertyName("state")] string State
);
src/Integrations/HomeAssistant/HomeAssistant.csproj
+11
-0
diff --git a/src/Integrations/HomeAssistant/HomeAssistant.csproj b/src/Integrations/HomeAssistant/HomeAssistant.csproj
new file mode 100644
index 0000000..e59519d
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>MMirror.Integrations.HomeAssistant</RootNamespace>
<AssemblyName>MMirror.Integrations.HomeAssistant</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
</ItemGroup>
</Project>
src/Integrations/HomeAssistant/HomeAssistantClient.cs
+48
-0
diff --git a/src/Integrations/HomeAssistant/HomeAssistantClient.cs b/src/Integrations/HomeAssistant/HomeAssistantClient.cs
new file mode 100644
index 0000000..4826455
@@ -0,0 +1,48 @@
using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace MMirror.Integrations.HomeAssistant;
public sealed class HomeAssistantClient(HttpClient httpClient, IOptions<HomeAssistantOptions> options)
{
public async Task<Temperatures> GetTemperatures(CancellationToken cancellationToken)
{
var states =
await httpClient.GetFromJsonAsync(
"/api/states",
HomeAssistantJsonSerializerContext.Default.EntityStateArray,
cancellationToken
) ?? throw new Exception("Received literal null from Home Assistant states endpoint.");
return new Temperatures(
FindTemperature(states, options.Value.InsideTemperatureEntityId),
FindTemperature(states, options.Value.OutsideTemperatureEntityId)
);
}
private static double FindTemperature(EntityState[] states, string entityId)
{
var state =
states.FirstOrDefault(entry => entry.EntityId == entityId)?.State
?? throw new Exception($"Home Assistant state not found for entity '{entityId}'.");
if (
!double.TryParse(state, NumberStyles.Float, CultureInfo.InvariantCulture, out var temperature)
|| double.IsNaN(temperature)
|| double.IsInfinity(temperature)
)
{
throw new Exception($"Home Assistant state '{state}' for entity '{entityId}' is not a valid temperature.");
}
return temperature;
}
}
public sealed record Temperatures(double InsideCelsius, double OutsideCelsius);
src/Integrations/HomeAssistant/HomeAssistantOptions.cs
+23
-0
diff --git a/src/Integrations/HomeAssistant/HomeAssistantOptions.cs b/src/Integrations/HomeAssistant/HomeAssistantOptions.cs
new file mode 100644
index 0000000..9e89afe
@@ -0,0 +1,23 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;
namespace MMirror.Integrations.HomeAssistant;
public sealed class HomeAssistantOptions
{
[Required]
public required Uri BaseAddress { get; set; }
[Required]
public required string Token { get; set; }
[Required]
public required string InsideTemperatureEntityId { get; set; }
[Required]
public required string OutsideTemperatureEntityId { get; set; }
}
[OptionsValidator]
internal sealed partial class HomeAssistantOptionsValidator : IValidateOptions<HomeAssistantOptions>;
src/Integrations/HomeAssistant/ServiceCollectionExtensions.cs
+34
-0
diff --git a/src/Integrations/HomeAssistant/ServiceCollectionExtensions.cs b/src/Integrations/HomeAssistant/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..13f989a
@@ -0,0 +1,34 @@
using System.Net.Http.Headers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace MMirror.Integrations.HomeAssistant;
public static class ServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddHomeAssistantServices()
{
services.AddOptions<HomeAssistantOptions>().BindConfiguration("HomeAssistant").ValidateOnStart();
services.AddTransient<IValidateOptions<HomeAssistantOptions>, HomeAssistantOptionsValidator>();
services
.AddHttpClient<HomeAssistantClient>()
.ConfigureHttpClient(
(sp, client) =>
{
var homeAssistantOptions = sp.GetRequiredService<IOptions<HomeAssistantOptions>>().Value;
client.BaseAddress = homeAssistantOptions.BaseAddress;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer",
homeAssistantOptions.Token
);
}
)
.AddStandardResilienceHandler();
return services;
}
}
}
src/Integrations/HomeAssistant/packages.lock.json
+198
-0
diff --git a/src/Integrations/HomeAssistant/packages.lock.json b/src/Integrations/HomeAssistant/packages.lock.json
new file mode 100644
index 0000000..31f9406
@@ -0,0 +1,198 @@
{
"version": 2,
"dependencies": {
"net11.0": {
"CSharpier.MsBuild": {
"type": "Direct",
"requested": "[1.2.6, )",
"resolved": "1.2.6",
"contentHash": "KMSJG+jfk7vjP52QkWB99qWespXCPAzG/IaMCMRHYWumJEAGKQYm2HtyWG6eqnOwDitH96i1cqq5EVesyOtPmg=="
},
"Microsoft.Extensions.Http": {
"type": "Direct",
"requested": "[11.0.0-preview.4.26230.115, )",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "HTY8+4kSEcmghrSTifim/cwZ4hQkniUr1+c1OMLHS1DJ2d7R9FFQ/t7Wf/MYTRHlB85ng9hMQNMY/PWTIfEmKQ==",
"dependencies": {
"Microsoft.Extensions.Diagnostics": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Http.Resilience": {
"type": "Direct",
"requested": "[10.6.0, )",
"resolved": "10.6.0",
"contentHash": "Diw9HdFABHx0EQTjfanXKuV6zSEyHEEH11ZGdsNxNf/3gsLEiBPXUximDG0EwYt1iIalH0JFlI1DXqpE4VjDpQ==",
"dependencies": {
"Microsoft.Extensions.Http.Diagnostics": "10.6.0",
"Microsoft.Extensions.ObjectPool": "10.0.8",
"Microsoft.Extensions.Resilience": "10.6.0"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Direct",
"requested": "[11.0.0-preview.4.26230.115, )",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "9QNxho6sxC4RXafVZGWzYq6KcYVpp1FkHtL+HJ967UNyUjhOWnGKMM0dPXkHKtlZ/0GGEDqnBDun0kuF633a6g==",
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.AmbientMetadata.Application": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "MPAI1c5QqkDgEyEvsSj7KPPWHlTCqvXxtfp7JISul4P54GfDkSs+S7Vyq7tmS7MzpEjuH/6xrsFlUqAPlLyTUA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.8"
}
},
"Microsoft.Extensions.Compliance.Abstractions": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "L8zTKn8e2LCQbsDFLWFm6fZQ54F/1FisLx43nkEof4HmmsO2HaZHshV85+qF8HXO48MlGJdrWUg+uVBj/WDmmw==",
"dependencies": {
"Microsoft.Extensions.ObjectPool": "10.0.8"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "CPb3DPC6kcgx+kuSTJ55H/A6Z+IZeW++VM+UiFpiwq6eQC2Mk3gHcFpqdQoK2MjWovWYN4Sz6n1RXhLWHVB35w=="
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "IOkgB4CnLUlKH+l0slH5Q+6m9pWfcCBocFPHc7czATwIx5jnVPgUMRj2gGhZtvGs1PKpeoZv7PCGK5cYIwlZzw==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.DependencyInjection.AutoActivation": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "FoX/prFoqjogPV1DleOnakFM9yzu8m4dK8Z0d9f8pPTf00PkEvd2Vulncijd/rykUX3aXERMMxsX6c/9QSAOaA=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "L1+sxK5JT7X5jvZ0RInbDnyJZG9rkp4cbgzoL0aGRUOOMp3PkgCcUjAUhAprxBu9LgGdeqZg7JratDMHS1YN/Q==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Options.ConfigurationExtensions": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Diagnostics.ExceptionSummarization": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "dpkWVFSg8JT1BBL33+lF3pY52En3y8qoHvtc5esMx8TVrxRXuzqIjca6MqyODpGfg5/NK4bpOYZDYBOAr8PSrA=="
},
"Microsoft.Extensions.Http.Diagnostics": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "p7nJYUYk5M0k1bWeRYQr/6k5KO2XpmUIIHgmlFZGirZJxXEShSz66Q5OX2Y8Yfaz9bIhSNqAbnn5B3zhNR2Sxg==",
"dependencies": {
"Microsoft.Extensions.Http": "10.0.8",
"Microsoft.Extensions.Telemetry": "10.6.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "EpZS5qqhsNXwgeSCl2m1VkysA+pTh3qlhyjkjiu0peZt/V7IyANJXQQUuv3y2kp/jzd9XdfzuUJU0Fgp7rcDGg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
"resolved": "10.0.8",
"contentHash": "rxSLTO7xTbcC3DuEJHNEijBr8g14Jj62zQ+DeFu68bsoTYoU8jLcMhc1735PV21bESXsATlL5LsfaWH71FOWAg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Binder": "10.0.8",
"Microsoft.Extensions.Logging": "10.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.8"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "10.0.8",
"contentHash": "aQBFbY8i/dacE0fP+ZJ8Lhx/unYRnGHhtM+tHb46GLkeNjBdOzgFk88sX6BVZBhoa6JrYIOBGYTc5K4WItBsag=="
},
"Microsoft.Extensions.Resilience": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "n3ARODKBYCZAJEaDiWqXQnrjh9FXBs1yTwmWUUNgvLdJmLo/MTtz60M2t1By1jIt+I3u82/W4L5xJy8abzPUyw==",
"dependencies": {
"Microsoft.Extensions.Diagnostics": "10.0.8",
"Microsoft.Extensions.Diagnostics.ExceptionSummarization": "10.6.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.8",
"Microsoft.Extensions.Telemetry.Abstractions": "10.6.0",
"Polly.Extensions": "8.4.2",
"Polly.RateLimiting": "8.4.2"
}
},
"Microsoft.Extensions.Telemetry": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "wbcC4zPaEGG53znXd4VDw3ZGITX352q80oW1iyK8rOGaJNof1zimVfZjn6E/Glw0ttZoiyHN0wf8b4KRk/rMAw==",
"dependencies": {
"Microsoft.Extensions.AmbientMetadata.Application": "10.6.0",
"Microsoft.Extensions.DependencyInjection.AutoActivation": "10.6.0",
"Microsoft.Extensions.Logging.Configuration": "10.0.8",
"Microsoft.Extensions.ObjectPool": "10.0.8",
"Microsoft.Extensions.Telemetry.Abstractions": "10.6.0"
}
},
"Microsoft.Extensions.Telemetry.Abstractions": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "aNQEJu5DD2YVQEWWmC/ALEiV1Qt400BaDO+SExtfAaGqYaNu/r2sW9xGLuc71fcjbrmzqX8LzNgK5mzjjMW9RQ==",
"dependencies": {
"Microsoft.Extensions.Compliance.Abstractions": "10.6.0",
"Microsoft.Extensions.ObjectPool": "10.0.8"
}
},
"Polly.Core": {
"type": "Transitive",
"resolved": "8.4.2",
"contentHash": "BpE2I6HBYYA5tF0Vn4eoQOGYTYIK1BlF5EXVgkWGn3mqUUjbXAr13J6fZVbp7Q3epRR8yshacBMlsHMhpOiV3g=="
},
"Polly.Extensions": {
"type": "Transitive",
"resolved": "8.4.2",
"contentHash": "GZ9vRVmR0jV2JtZavt+pGUsQ1O1cuRKG7R7VOZI6ZDy9y6RNPvRvXK1tuS4ffUrv8L0FTea59oEuQzgS0R7zSA==",
"dependencies": {
"Polly.Core": "8.4.2"
}
},
"Polly.RateLimiting": {
"type": "Transitive",
"resolved": "8.4.2",
"contentHash": "ehTImQ/eUyO07VYW2WvwSmU9rRH200SKJ/3jku9rOkyWE0A2JxNFmAVms8dSn49QLSjmjFRRSgfNyOgr/2PSmA==",
"dependencies": {
"Polly.Core": "8.4.2",
"System.Threading.RateLimiting": "8.0.0"
}
},
"System.Threading.RateLimiting": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q=="
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[11.0.0-preview.4.26230.115, )",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "v+F34r5W5XgR95xG0Te2KyYmCDnkc4kljLMGEZ30WKQjAzuXz/pLTEvsRNYxNJUzfKPI0H4oqLglyHZkCe1lYw=="
}
},
"net11.0/linux-arm64": {},
"net11.0/linux-x64": {},
"net11.0/osx-arm64": {},
"net11.0/osx-x64": {},
"net11.0/win-arm64": {},
"net11.0/win-x64": {}
}
}
\ No newline at end of file
src/Integrations/Smhi/SmhiClient.cs
+4
-1
diff --git a/src/Integrations/Smhi/SmhiClient.cs b/src/Integrations/Smhi/SmhiClient.cs
index 7fdd703..c5e09de 100644
@@ -172,7 +172,10 @@ public sealed record ForecastPeriod(
double TemperatureCelsius,
double PrecipitationMillimeters,
WeatherSymbol Symbol
);
)
{
public double? InsideTemperatureCelsius { get; init; } = null;
};
public enum WeatherSymbol
{
tests/Integrations/HomeAssistant/HomeAssistant.csproj
+19
-0
diff --git a/tests/Integrations/HomeAssistant/HomeAssistant.csproj b/tests/Integrations/HomeAssistant/HomeAssistant.csproj
new file mode 100644
index 0000000..b739326
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>MMirror.Integrations.HomeAssistant.Tests</RootNamespace>
<AssemblyName>MMirror.Integrations.HomeAssistant.Tests</AssemblyName>
<UserSecretsId>a3f8c2d1-7b4e-4a9f-9c6d-1e2f3a4b5c6d</UserSecretsId>
<EnableMSTestRunner>true</EnableMSTestRunner>
<OutputType>Exe</OutputType>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Integrations\HomeAssistant\HomeAssistant.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="MSTest" />
<PackageReference Include="Shouldly" />
</ItemGroup>
</Project>
tests/Integrations/HomeAssistant/HomeAssistantClientTests/IntegrationTests.cs
+27
-0
diff --git a/tests/Integrations/HomeAssistant/HomeAssistantClientTests/IntegrationTests.cs b/tests/Integrations/HomeAssistant/HomeAssistantClientTests/IntegrationTests.cs
new file mode 100644
index 0000000..90a1eaa
@@ -0,0 +1,27 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
namespace MMirror.Integrations.HomeAssistant.Tests.HomeAssistantClientTests;
[TestClass]
public sealed class IntegrationTests
{
[TestMethod]
public async Task IntegrationTest()
{
var host = Host.CreateApplicationBuilder();
host.Configuration.AddUserSecrets<IntegrationTests>();
host.Services.AddHomeAssistantServices();
var app = host.Build();
var client = app.Services.GetRequiredService<HomeAssistantClient>();
var temperatures = await client.GetTemperatures(CancellationToken.None);
temperatures.InsideCelsius.ShouldBeInRange(-50, 50);
temperatures.OutsideCelsius.ShouldBeInRange(-50, 50);
}
}
tests/Integrations/HomeAssistant/MSTestSettings.cs
+3
-0
diff --git a/tests/Integrations/HomeAssistant/MSTestSettings.cs b/tests/Integrations/HomeAssistant/MSTestSettings.cs
new file mode 100644
index 0000000..ae411c7
@@ -0,0 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
tests/Integrations/HomeAssistant/README.md
+7
-0
diff --git a/tests/Integrations/HomeAssistant/README.md b/tests/Integrations/HomeAssistant/README.md
new file mode 100644
index 0000000..8e6b92b
@@ -0,0 +1,7 @@
# Home Assistant tests
These are integration tests for the Home Assistant integration.
## Setup
Provide the Home Assistant base address, long-lived access token, and inside/outside temperature entity IDs as User Secrets before running the tests.
tests/Integrations/HomeAssistant/packages.lock.json
+651
-0
diff --git a/tests/Integrations/HomeAssistant/packages.lock.json b/tests/Integrations/HomeAssistant/packages.lock.json
new file mode 100644
index 0000000..4fa5336
@@ -0,0 +1,651 @@
{
"version": 2,
"dependencies": {
"net11.0": {
"CSharpier.MsBuild": {
"type": "Direct",
"requested": "[1.2.6, )",
"resolved": "1.2.6",
"contentHash": "KMSJG+jfk7vjP52QkWB99qWespXCPAzG/IaMCMRHYWumJEAGKQYm2HtyWG6eqnOwDitH96i1cqq5EVesyOtPmg=="
},
"Microsoft.Extensions.Hosting": {
"type": "Direct",
"requested": "[11.0.0-preview.4.26230.115, )",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "JabRZnPjt57VHxcOm7G7runakIkv3PunM2GTJF32KF5hJChgpUh2gnuL4VQ0GsVtHXGsbefj9tddYs2K0YJmpA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.Binder": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.CommandLine": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.FileExtensions": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.Json": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.UserSecrets": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.DependencyInjection": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Diagnostics": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.FileProviders.Physical": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging.Configuration": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging.Console": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging.Debug": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging.EventLog": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging.EventSource": "11.0.0-preview.4.26230.115"
}
},
"MSTest": {
"type": "Direct",
"requested": "[4.2.3, )",
"resolved": "4.2.3",
"contentHash": "fUaw2rGyhaSMRuAibJo+1PQkI4PHS1FZx/tASq1Lr3eZk5NlshQRyBBdUkhnvfvOG+tCKKy3T/BnMiI47qeZrw==",
"dependencies": {
"MSTest.TestAdapter": "4.2.3",
"MSTest.TestFramework": "4.2.3",
"Microsoft.NET.Test.Sdk": "18.3.0",
"Microsoft.Testing.Extensions.CodeCoverage": "18.5.2",
"Microsoft.Testing.Extensions.TrxReport": "2.2.3"
}
},
"Shouldly": {
"type": "Direct",
"requested": "[4.3.0, )",
"resolved": "4.3.0",
"contentHash": "sDetrWXrl6YXZ4HeLsdBoNk3uIa7K+V4uvIJ+cqdRa5DrFxeTED7VkjoxCuU1kJWpUuBDZz2QXFzSxBtVXLwRQ==",
"dependencies": {
"DiffEngine": "11.3.0",
"EmptyFiles": "4.4.0"
}
},
"DiffEngine": {
"type": "Transitive",
"resolved": "11.3.0",
"contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==",
"dependencies": {
"EmptyFiles": "4.4.0",
"System.Management": "6.0.1"
}
},
"EmptyFiles": {
"type": "Transitive",
"resolved": "4.4.0",
"contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw=="
},
"Microsoft.ApplicationInsights": {
"type": "Transitive",
"resolved": "2.23.0",
"contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw=="
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "18.3.0",
"contentHash": "23BNy/vziREC20Wwhb50K7+kZe0m07KlLWDQv4qjJ9tt3QjpDpDIqJFrhYHmMEo9xDkuSp55U/8h4bMF7MiB+g=="
},
"Microsoft.DiaSymReader": {
"type": "Transitive",
"resolved": "2.2.3",
"contentHash": "bhwzJfzyiJM0nXJyNB7Y9OfsEXyxLdDBHG99soIp5JjnPydwkOaBdRCtRtWgQh3noSLi2cSIZ/wpbHNNE9knxQ=="
},
"Microsoft.Extensions.AmbientMetadata.Application": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "MPAI1c5QqkDgEyEvsSj7KPPWHlTCqvXxtfp7JISul4P54GfDkSs+S7Vyq7tmS7MzpEjuH/6xrsFlUqAPlLyTUA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.8"
}
},
"Microsoft.Extensions.Compliance.Abstractions": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "L8zTKn8e2LCQbsDFLWFm6fZQ54F/1FisLx43nkEof4HmmsO2HaZHshV85+qF8HXO48MlGJdrWUg+uVBj/WDmmw==",
"dependencies": {
"Microsoft.Extensions.ObjectPool": "10.0.8"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "CPb3DPC6kcgx+kuSTJ55H/A6Z+IZeW++VM+UiFpiwq6eQC2Mk3gHcFpqdQoK2MjWovWYN4Sz6n1RXhLWHVB35w=="
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "IOkgB4CnLUlKH+l0slH5Q+6m9pWfcCBocFPHc7czATwIx5jnVPgUMRj2gGhZtvGs1PKpeoZv7PCGK5cYIwlZzw==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Configuration.CommandLine": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "AWgDyrMol+ywzG0MAAk+TkLIy4fMEsOCG1E/zb1kHE3Lp81GNNEMU+jOJTF1gEsLkDajY5AouNAWx3cvi+pxzg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Configuration.EnvironmentVariables": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "7vBrAODuFr5u548MNTaUybxQ+Hw/x9boZ2rWQynOS/KJkZhrriUFtgbQTeRamQFVna1eh78WjVAqEKm8GpQXBg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Configuration.FileExtensions": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "//7zz5cgmkd/cfgKanPna9CHJ4o9Wp9JPpr+3rmSa7vd/+Wwxjb9Z9sUKvH0LsJl72LnUYWKJ7Ah02nr2yLsYA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.FileProviders.Physical": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Configuration.Json": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "cXhGyFCd2LnPAMu5pVxf3CeIqYJ+wy866tNKjaAvLFQsJ1QDB4Xfmiy+6q0Zm8DvjektmB/IoDBl5F/0AKEBOA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.FileExtensions": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Configuration.UserSecrets": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "GnJpXFEumu46mGMU++s54qgRwlkGhCP05xHOSgaAK/lLTUOt2mAMaIwFqrLFKfUFtkByjxSe/1FtzXx2zrIv9w==",
"dependencies": {
"Microsoft.Extensions.Configuration.Json": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.FileProviders.Physical": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.DependencyInjection.AutoActivation": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "FoX/prFoqjogPV1DleOnakFM9yzu8m4dK8Z0d9f8pPTf00PkEvd2Vulncijd/rykUX3aXERMMxsX6c/9QSAOaA=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "8.0.2",
"contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "L1+sxK5JT7X5jvZ0RInbDnyJZG9rkp4cbgzoL0aGRUOOMp3PkgCcUjAUhAprxBu9LgGdeqZg7JratDMHS1YN/Q==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Options.ConfigurationExtensions": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Diagnostics.ExceptionSummarization": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "dpkWVFSg8JT1BBL33+lF3pY52En3y8qoHvtc5esMx8TVrxRXuzqIjca6MqyODpGfg5/NK4bpOYZDYBOAr8PSrA=="
},
"Microsoft.Extensions.FileProviders.Physical": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "L/+w2lo0fV2hcQuAe3YIBwXl4YP16c/xFUCHXjhzidByFbyECb2H8hJ5EEeszihGcT69nd+gJqrZyzyp1bObig==",
"dependencies": {
"Microsoft.Extensions.FileSystemGlobbing": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "y8hEW4rVMESwL9iMfc0PICy0ScXzj4MMXclEjf5AXZKOFrJe6nu8iU7K9VVZenNweXdLYvlcs2ZSnhVz7h8oYg=="
},
"Microsoft.Extensions.Http.Diagnostics": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "p7nJYUYk5M0k1bWeRYQr/6k5KO2XpmUIIHgmlFZGirZJxXEShSz66Q5OX2Y8Yfaz9bIhSNqAbnn5B3zhNR2Sxg==",
"dependencies": {
"Microsoft.Extensions.Http": "10.0.8",
"Microsoft.Extensions.Telemetry": "10.6.0"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "EpZS5qqhsNXwgeSCl2m1VkysA+pTh3qlhyjkjiu0peZt/V7IyANJXQQUuv3y2kp/jzd9XdfzuUJU0Fgp7rcDGg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "YkVeVCrOfKdYghClaYH/jcC2Irx6gtrWPAOjNkOeITQVQ41vvhPVp7EcXYD8IcfYH58YIS5WiX1ZJu6uLEVrAQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Configuration.Binder": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Options.ConfigurationExtensions": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Logging.Console": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "1ATxqXTZnR5M4tOeCtZjA+fw1b+jWPsmUAp3PUCCvndV5ghIcSvnwZ5RiQFoTtYFSdNP+pMoxCtviI71/xyRCw==",
"dependencies": {
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging.Configuration": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Logging.Debug": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "kD1p/UZ/NqHTLt1iLjtkJpPtPWWziLkV6kG3TxLi67gWasixyvH1grMfkd14XR24RIooTkavxGZJV+xjeSSsiA==",
"dependencies": {
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Logging.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "DOtIsmOENF8xmQi0uw0sx4/1aDHDgj1PuGmlom2zobrqbJp2b5fkZPCIOTmPZsXbk78nxVnPH7/NTbR1cYRnsA==",
"dependencies": {
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging.Configuration": "11.0.0-preview.4.26230.115",
"System.Diagnostics.EventLog": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Logging.EventSource": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "koDYnhbSTo+di3NMq13c0jhYFTIc7XUgnevONLwDpzR2GbbXfUR+dN5GuMeAVR4EQljUw5OYllrYflPNb0zoZA==",
"dependencies": {
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "10.0.8",
"contentHash": "aQBFbY8i/dacE0fP+ZJ8Lhx/unYRnGHhtM+tHb46GLkeNjBdOzgFk88sX6BVZBhoa6JrYIOBGYTc5K4WItBsag=="
},
"Microsoft.Extensions.Resilience": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "n3ARODKBYCZAJEaDiWqXQnrjh9FXBs1yTwmWUUNgvLdJmLo/MTtz60M2t1By1jIt+I3u82/W4L5xJy8abzPUyw==",
"dependencies": {
"Microsoft.Extensions.Diagnostics": "10.0.8",
"Microsoft.Extensions.Diagnostics.ExceptionSummarization": "10.6.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.8",
"Microsoft.Extensions.Telemetry.Abstractions": "10.6.0",
"Polly.Extensions": "8.4.2",
"Polly.RateLimiting": "8.4.2"
}
},
"Microsoft.Extensions.Telemetry": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "wbcC4zPaEGG53znXd4VDw3ZGITX352q80oW1iyK8rOGaJNof1zimVfZjn6E/Glw0ttZoiyHN0wf8b4KRk/rMAw==",
"dependencies": {
"Microsoft.Extensions.AmbientMetadata.Application": "10.6.0",
"Microsoft.Extensions.DependencyInjection.AutoActivation": "10.6.0",
"Microsoft.Extensions.Logging.Configuration": "10.0.8",
"Microsoft.Extensions.ObjectPool": "10.0.8",
"Microsoft.Extensions.Telemetry.Abstractions": "10.6.0"
}
},
"Microsoft.Extensions.Telemetry.Abstractions": {
"type": "Transitive",
"resolved": "10.6.0",
"contentHash": "aNQEJu5DD2YVQEWWmC/ALEiV1Qt400BaDO+SExtfAaGqYaNu/r2sW9xGLuc71fcjbrmzqX8LzNgK5mzjjMW9RQ==",
"dependencies": {
"Microsoft.Extensions.Compliance.Abstractions": "10.6.0",
"Microsoft.Extensions.ObjectPool": "10.0.8"
}
},
"Microsoft.NET.Test.Sdk": {
"type": "Transitive",
"resolved": "18.3.0",
"contentHash": "xW3kXuWRQtgoxJp4J+gdhHSQyK+6Wb/AZDSd7lMvuMRYlZ1tnpkojyfZlWilB5G4dmZ0Y0ZxU/M23TlubndNkw==",
"dependencies": {
"Microsoft.CodeCoverage": "18.3.0",
"Microsoft.TestPlatform.TestHost": "18.3.0"
}
},
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Transitive",
"resolved": "18.5.2",
"contentHash": "UNcGLx9pVtlXF8MPDR8KDp+/OKKNIJjpzwRyZSt609TSGvaD8mtuQMb5GKZvhMucPp0a5Juvn3kxXDceQZWmAg==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.3",
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"Microsoft.Testing.Extensions.Telemetry": {
"type": "Transitive",
"resolved": "2.2.3",
"contentHash": "mLdW+JOR3kXYGTdgR/qc/UZBA0r+eCR2k6bUxTcuDj5w9WdIQ7Lol5MBUU7YOSGd9bs9bvhSYWAptgz0YtQqCA==",
"dependencies": {
"Microsoft.ApplicationInsights": "2.23.0",
"Microsoft.Testing.Platform": "2.2.3"
}
},
"Microsoft.Testing.Extensions.TrxReport": {
"type": "Transitive",
"resolved": "2.2.3",
"contentHash": "9Hot3ty5ZVWHrW40k2NPfD0dCaPwIxj7j7VjujNYwpYkYw9AdbejPHjGNkL/gvUWorauJf5IkeDoUeIbS7LuUg==",
"dependencies": {
"Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.3",
"Microsoft.Testing.Platform": "2.2.3"
}
},
"Microsoft.Testing.Extensions.TrxReport.Abstractions": {
"type": "Transitive",
"resolved": "2.2.3",
"contentHash": "hntvxJEkmUAx6C2xXc/PO38DqEQl4rimzOgSvTR1hAMruMid7R4RcXOrzzF33J66gKaN7jRaQ0TMW/nNfaV9jw==",
"dependencies": {
"Microsoft.Testing.Platform": "2.2.3"
}
},
"Microsoft.Testing.Extensions.VSTestBridge": {
"type": "Transitive",
"resolved": "2.2.3",
"contentHash": "7WlJISO8QKUK+d+WhgnANwy4ACwUrvICnviY/mthPwjZ2gVeDaSUAeBnMy2cxfzZgm8VATtGUDbYzUxsgV2CyQ==",
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "18.3.0",
"Microsoft.Testing.Extensions.Telemetry": "2.2.3",
"Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.3",
"Microsoft.Testing.Platform": "2.2.3"
}
},
"Microsoft.Testing.Platform": {
"type": "Transitive",
"resolved": "2.2.3",
"contentHash": "LhM1/Qoi8Ams5QcD4r3f09CSOono9iQr3NEJQItFtyzWB55nWTgEOsVqXqMWWWIwk3nkPqc+XfnlJmp8xUI5fg=="
},
"Microsoft.Testing.Platform.MSBuild": {
"type": "Transitive",
"resolved": "2.2.3",
"contentHash": "Q22jJYJLx4srTinsAuoCskqmzjrBJC8YeGJMHHIcrf1dQeHoEZ7wsqDzTlENkMoke2qfufF7U+9u58nlZunH/Q==",
"dependencies": {
"Microsoft.Testing.Platform": "2.2.3"
}
},
"Microsoft.TestPlatform.ObjectModel": {
"type": "Transitive",
"resolved": "18.3.0",
"contentHash": "AEIEX2aWdPO9XbtR96eBaJxmXRD9vaI9uQ1T/JbPEKlTAZwYx0ZrMzKyULMdh/HH9Sg03kXCoN7LszQ90o6nPQ=="
},
"Microsoft.TestPlatform.TestHost": {
"type": "Transitive",
"resolved": "18.3.0",
"contentHash": "twmsoelXnp1uWMU3VGip9f0Jr1mZ0PZqgJdF35CIrdYgYrkHIJMV1m8uKyhcdjLdsQDESHAgkR7KhS9i1qpJag==",
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "18.3.0",
"Newtonsoft.Json": "13.0.3"
}
},
"MSTest.Analyzers": {
"type": "Transitive",
"resolved": "4.2.3",
"contentHash": "dxOZt8/LWuiox7rugInJoIa5Mmu3pBmXdfaoZOx/mxx8+sUFFpjBXPlWXQXGeWzpkVPNC3x1Jf7rt2h2Zjyvvg=="
},
"MSTest.TestAdapter": {
"type": "Transitive",
"resolved": "4.2.3",
"contentHash": "oVV/luk0bBghnVvLaw8MwFlD7It0Cx9P2nKobeqIafmTQqFFWY69Wo801dxjeNaLzO/o9WQ84WSK84gXJuubhg==",
"dependencies": {
"MSTest.TestFramework": "4.2.3",
"Microsoft.Testing.Extensions.VSTestBridge": "2.2.3",
"Microsoft.Testing.Platform.MSBuild": "2.2.3"
}
},
"MSTest.TestFramework": {
"type": "Transitive",
"resolved": "4.2.3",
"contentHash": "9zzij59YLh+tf+FRLNqhzHjmdspR91bol+jdQxLlxxTGMAML6LDbvuyXKMGdcrE84+QpKkk6KjTVBC5PBGrDeA==",
"dependencies": {
"MSTest.Analyzers": "4.2.3"
}
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.3",
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
},
"Polly.Core": {
"type": "Transitive",
"resolved": "8.4.2",
"contentHash": "BpE2I6HBYYA5tF0Vn4eoQOGYTYIK1BlF5EXVgkWGn3mqUUjbXAr13J6fZVbp7Q3epRR8yshacBMlsHMhpOiV3g=="
},
"Polly.Extensions": {
"type": "Transitive",
"resolved": "8.4.2",
"contentHash": "GZ9vRVmR0jV2JtZavt+pGUsQ1O1cuRKG7R7VOZI6ZDy9y6RNPvRvXK1tuS4ffUrv8L0FTea59oEuQzgS0R7zSA==",
"dependencies": {
"Polly.Core": "8.4.2"
}
},
"Polly.RateLimiting": {
"type": "Transitive",
"resolved": "8.4.2",
"contentHash": "ehTImQ/eUyO07VYW2WvwSmU9rRH200SKJ/3jku9rOkyWE0A2JxNFmAVms8dSn49QLSjmjFRRSgfNyOgr/2PSmA==",
"dependencies": {
"Polly.Core": "8.4.2",
"System.Threading.RateLimiting": "8.0.0"
}
},
"System.CodeDom": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "/o76q92uK1eaosCQrIjp/V4OBHs9zIXxH+8ZEMFZg1GVH7fI24GUPx1BLawLCmdq/U1AdGeoto716k4BJEhi2g=="
},
"System.Management": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
},
"System.Threading.RateLimiting": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q=="
},
"MMirror.Integrations.HomeAssistant": {
"type": "Project",
"dependencies": {
"Microsoft.Extensions.Http": "[11.0.0-preview.4.26230.115, )",
"Microsoft.Extensions.Http.Resilience": "[10.6.0, )",
"Microsoft.Extensions.Options.ConfigurationExtensions": "[11.0.0-preview.4.26230.115, )"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "CentralTransitive",
"requested": "[11.0.0-preview.4.26230.115, )",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "v+F34r5W5XgR95xG0Te2KyYmCDnkc4kljLMGEZ30WKQjAzuXz/pLTEvsRNYxNJUzfKPI0H4oqLglyHZkCe1lYw=="
},
"Microsoft.Extensions.Http": {
"type": "CentralTransitive",
"requested": "[11.0.0-preview.4.26230.115, )",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "HTY8+4kSEcmghrSTifim/cwZ4hQkniUr1+c1OMLHS1DJ2d7R9FFQ/t7Wf/MYTRHlB85ng9hMQNMY/PWTIfEmKQ==",
"dependencies": {
"Microsoft.Extensions.Diagnostics": "11.0.0-preview.4.26230.115",
"Microsoft.Extensions.Logging": "11.0.0-preview.4.26230.115"
}
},
"Microsoft.Extensions.Http.Resilience": {
"type": "CentralTransitive",
"requested": "[10.6.0, )",
"resolved": "10.6.0",
"contentHash": "Diw9HdFABHx0EQTjfanXKuV6zSEyHEEH11ZGdsNxNf/3gsLEiBPXUximDG0EwYt1iIalH0JFlI1DXqpE4VjDpQ==",
"dependencies": {
"Microsoft.Extensions.Http.Diagnostics": "10.6.0",
"Microsoft.Extensions.ObjectPool": "10.0.8",
"Microsoft.Extensions.Resilience": "10.6.0"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "CentralTransitive",
"requested": "[11.0.0-preview.4.26230.115, )",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "9QNxho6sxC4RXafVZGWzYq6KcYVpp1FkHtL+HJ967UNyUjhOWnGKMM0dPXkHKtlZ/0GGEDqnBDun0kuF633a6g==",
"dependencies": {
"Microsoft.Extensions.Configuration.Binder": "11.0.0-preview.4.26230.115"
}
}
},
"net11.0/linux-arm64": {
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Transitive",
"resolved": "18.5.2",
"contentHash": "UNcGLx9pVtlXF8MPDR8KDp+/OKKNIJjpzwRyZSt609TSGvaD8mtuQMb5GKZvhMucPp0a5Juvn3kxXDceQZWmAg==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.3",
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "/o76q92uK1eaosCQrIjp/V4OBHs9zIXxH+8ZEMFZg1GVH7fI24GUPx1BLawLCmdq/U1AdGeoto716k4BJEhi2g=="
},
"System.Management": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
}
},
"net11.0/linux-x64": {
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Transitive",
"resolved": "18.5.2",
"contentHash": "UNcGLx9pVtlXF8MPDR8KDp+/OKKNIJjpzwRyZSt609TSGvaD8mtuQMb5GKZvhMucPp0a5Juvn3kxXDceQZWmAg==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.3",
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "/o76q92uK1eaosCQrIjp/V4OBHs9zIXxH+8ZEMFZg1GVH7fI24GUPx1BLawLCmdq/U1AdGeoto716k4BJEhi2g=="
},
"System.Management": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
}
},
"net11.0/osx-arm64": {
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Transitive",
"resolved": "18.5.2",
"contentHash": "UNcGLx9pVtlXF8MPDR8KDp+/OKKNIJjpzwRyZSt609TSGvaD8mtuQMb5GKZvhMucPp0a5Juvn3kxXDceQZWmAg==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.3",
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "/o76q92uK1eaosCQrIjp/V4OBHs9zIXxH+8ZEMFZg1GVH7fI24GUPx1BLawLCmdq/U1AdGeoto716k4BJEhi2g=="
},
"System.Management": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
}
},
"net11.0/osx-x64": {
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Transitive",
"resolved": "18.5.2",
"contentHash": "UNcGLx9pVtlXF8MPDR8KDp+/OKKNIJjpzwRyZSt609TSGvaD8mtuQMb5GKZvhMucPp0a5Juvn3kxXDceQZWmAg==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.3",
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "/o76q92uK1eaosCQrIjp/V4OBHs9zIXxH+8ZEMFZg1GVH7fI24GUPx1BLawLCmdq/U1AdGeoto716k4BJEhi2g=="
},
"System.Management": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
}
},
"net11.0/win-arm64": {
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Transitive",
"resolved": "18.5.2",
"contentHash": "UNcGLx9pVtlXF8MPDR8KDp+/OKKNIJjpzwRyZSt609TSGvaD8mtuQMb5GKZvhMucPp0a5Juvn3kxXDceQZWmAg==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.3",
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "/o76q92uK1eaosCQrIjp/V4OBHs9zIXxH+8ZEMFZg1GVH7fI24GUPx1BLawLCmdq/U1AdGeoto716k4BJEhi2g=="
},
"System.Management": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
}
},
"net11.0/win-x64": {
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Transitive",
"resolved": "18.5.2",
"contentHash": "UNcGLx9pVtlXF8MPDR8KDp+/OKKNIJjpzwRyZSt609TSGvaD8mtuQMb5GKZvhMucPp0a5Juvn3kxXDceQZWmAg==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.3",
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Platform": "2.1.0"
}
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "11.0.0-preview.4.26230.115",
"contentHash": "/o76q92uK1eaosCQrIjp/V4OBHs9zIXxH+8ZEMFZg1GVH7fI24GUPx1BLawLCmdq/U1AdGeoto716k4BJEhi2g=="
},
"System.Management": {
"type": "Transitive",
"resolved": "6.0.1",
"contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
}
}
}
}
\ No newline at end of file