Slider added

This commit is contained in:
audioprog 2025-08-16 20:11:06 +02:00
parent baa4d47313
commit 1418a0644d
5 changed files with 200 additions and 48 deletions

View file

@ -5,14 +5,20 @@
mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="50" mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="50"
x:Class="HAcontrol.MainWindow" x:Class="HAcontrol.MainWindow"
Title="HAcontrol" Title="HAcontrol"
Width="100" Height="50" Width="100" Height="500"
MinWidth="100" MinHeight="50" MinWidth="100" MinHeight="500"
MaxWidth="100" MaxHeight="50" MaxWidth="100" MaxHeight="500"
ExtendClientAreaToDecorationsHint="True" ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaTitleBarHeightHint="0"> ExtendClientAreaTitleBarHeightHint="0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="0"> <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="10">
<Button x:Name="StatusText" Content="Stream" Width="70" Height="50" Click="Button_Click"/>
<Button x:Name="SettingsButton" Content="..." Width="30" Height="50" Click="SettingsButton_Click"/> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="0">
<Button x:Name="StatusText" Content="Stream" Width="70" Height="50" Click="Button_Click"/>
<Button x:Name="SettingsButton" Content="..." Width="30" Height="50" Click="SettingsButton_Click"/>
</StackPanel>
<Slider x:Name="FaderControl" ValueChanged="Fader_ValueChanged" Orientation="Vertical" Maximum="100" Width="50" Height="400" PointerWheelChanged="FaderControl_PointerWheelChanged"/>
</StackPanel> </StackPanel>
</Window> </Window>

View file

@ -4,8 +4,9 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives;
namespace HAcontrol; namespace HAcontrol;
@ -15,10 +16,13 @@ public partial class MainWindow : Window
private System.Threading.CancellationTokenSource? _cts; private System.Threading.CancellationTokenSource? _cts;
private static MainWindow self; private static MainWindow? self;
public static MainWindow Self => self; public static MainWindow? Self => self;
public bool ShowFader => !string.IsNullOrEmpty(settings?.FaderEntity);
private PixelPoint? _windowPosition;
public MainWindow() public MainWindow()
{ {
@ -26,8 +30,17 @@ public partial class MainWindow : Window
InitializeComponent(); InitializeComponent();
Topmost = true;
LoadSettings(); LoadSettings();
// Restore window position if available
if (settings != null)
{
if (settings.WindowTop != 0 || settings.WindowLeft != 0)
this.Position = new Avalonia.PixelPoint((int)settings.WindowLeft, (int)settings.WindowTop);
}
_cts = new System.Threading.CancellationTokenSource(); _cts = new System.Threading.CancellationTokenSource();
StartStatePolling(_cts.Token); StartStatePolling(_cts.Token);
} }
@ -45,13 +58,28 @@ public partial class MainWindow : Window
var json = File.ReadAllText(SettingsWindow.SettingsFile); var json = File.ReadAllText(SettingsWindow.SettingsFile);
settings = JsonSerializer.Deserialize<Settings>(json); settings = JsonSerializer.Deserialize<Settings>(json);
} }
FaderControl.IsVisible = ShowFader;
var newHeight = ShowFader ? 500 : 100;
MaxHeight = newHeight;
MinHeight = newHeight;
Height = newHeight;
} }
protected override void OnClosed(EventArgs e) protected override void OnClosed(EventArgs e)
{ {
base.OnClosed(e); base.OnClosed(e);
// Save window position
if (settings != null && _windowPosition != null)
{
settings.WindowTop = _windowPosition.Value.Y;
settings.WindowLeft = _windowPosition.Value.X;
var json = System.Text.Json.JsonSerializer.Serialize(settings, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
System.IO.File.WriteAllText(SettingsWindow.SettingsFile, json);
}
_cts?.Cancel(); _cts?.Cancel();
_cts?.Dispose(); _cts?.Dispose();
self = null;
} }
private async void StartStatePolling(System.Threading.CancellationToken token) private async void StartStatePolling(System.Threading.CancellationToken token)
@ -98,6 +126,8 @@ public partial class MainWindow : Window
private async Task GetState() private async Task GetState()
{ {
_windowPosition = Position;
if (settings == null || string.IsNullOrEmpty(settings.HomeAssistantUrl) || string.IsNullOrEmpty(settings.Entity) || string.IsNullOrEmpty(settings.AccessToken)) if (settings == null || string.IsNullOrEmpty(settings.HomeAssistantUrl) || string.IsNullOrEmpty(settings.Entity) || string.IsNullOrEmpty(settings.AccessToken))
{ {
StatusText.Content = "Einstellungen fehlen!"; StatusText.Content = "Einstellungen fehlen!";
@ -116,27 +146,62 @@ public partial class MainWindow : Window
string statusUrl = $"{settings.HomeAssistantUrl}/api/states/{settings.Entity}"; string statusUrl = $"{settings.HomeAssistantUrl}/api/states/{settings.Entity}";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", settings.AccessToken); client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", settings.AccessToken);
using var response = await client.GetAsync(statusUrl); using (var response = await client.GetAsync(statusUrl))
string statusJson = await response.Content.ReadAsStringAsync(); {
string statusJson = await response.Content.ReadAsStringAsync();
// Parse the JSON and extract the state // Parse the JSON and extract the state
try try
{ {
var jsonDoc = System.Text.Json.JsonDocument.Parse(statusJson); var jsonDoc = JsonDocument.Parse(statusJson);
string? state = jsonDoc.RootElement.GetProperty("state").GetString(); string? state = jsonDoc.RootElement.GetProperty("state").GetString();
string? entity = jsonDoc.RootElement.GetProperty("entity_id").GetString(); string? entity = jsonDoc.RootElement.GetProperty("entity_id").GetString();
StatusText.Content = $"{state}"; StatusText.Content = $"{state}";
if (state == "on") if (state == "on")
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Green); StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Green);
else if (state == "off") else if (state == "off")
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Red);
else
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Orange);
}
catch
{
StatusText.Content = "Fehler!";
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Red); StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Red);
else }
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Orange);
} }
catch
if (!string.IsNullOrEmpty(settings.FaderEntity))
{ {
StatusText.Content = "Fehler!"; statusUrl = $"{settings.HomeAssistantUrl}/api/states/{settings.FaderEntity}";
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Red); using (var response = await client.GetAsync(statusUrl))
{
string statusJson = await response.Content.ReadAsStringAsync();
// Parse the JSON and extract the state
try
{
// {"entity_id":"number.wing_pp_control_main_4_fader","state":"20.0000002980232","attributes":{"min":0,"max":100,"step":1.0,"mode":"auto","db":"-38.0","low_precision":20.0,"attribution":"","icon":"mdi:volume-source","friendly_name":"Main 4 Fader"},"last_changed":"2025-08-16T14:14:32.455801+00:00","last_reported":"2025-08-16T14:14:32.455801+00:00","last_updated":"2025-08-16T14:14:32.455801+00:00","context":{"id":"01K2SKPTR48X14WGRN6AZDP4Y4","parent_id":null,"user_id":"d548e3619c124b8ead8043c4d6e9223b"}}
var jsonDoc = JsonDocument.Parse(statusJson);
string? state = jsonDoc.RootElement.GetProperty("state").GetString();
string? entity = jsonDoc.RootElement.GetProperty("entity_id").GetString();
if (double.TryParse(state, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out double faderValue))
{
FaderControl.Value = faderValue;
FaderControl.IsVisible = true;
}
else
{
FaderControl.Value = 0.0;
FaderControl.IsVisible = false;
}
}
catch
{
FaderControl.Value = 0.0;
FaderControl.IsVisible = false;
}
}
} }
} }
} }
@ -153,4 +218,76 @@ public partial class MainWindow : Window
var settingsWindow = new SettingsWindow(); var settingsWindow = new SettingsWindow();
settingsWindow.ShowDialog(this); settingsWindow.ShowDialog(this);
} }
private async void Fader_ValueChanged(object? sender, RangeBaseValueChangedEventArgs e)
{
if (settings == null || string.IsNullOrEmpty(settings.HomeAssistantUrl) || string.IsNullOrEmpty(settings.FaderEntity))
{
StatusText.Content = "Einstellungen fehlen!";
StatusText.Foreground = new Avalonia.Media.SolidColorBrush(Avalonia.Media.Colors.Red);
return;
}
string newState = e.NewValue.ToString(System.Globalization.CultureInfo.InvariantCulture);
await ChangeEntityState(settings.FaderEntity, newState);
}
private async Task ChangeEntityState(string entity, string newState)
{
if (settings == null || string.IsNullOrEmpty(settings.HomeAssistantUrl) || string.IsNullOrEmpty(settings.AccessToken))
{
Console.WriteLine("Einstellungen fehlen!");
return;
}
var url = $"{settings.HomeAssistantUrl}/api/states/{entity}";
var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true;
using var client = new HttpClient(handler);
// Setze den Authorization-Header
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", settings.AccessToken);
// Erstelle den JSON-Inhalt
var jsonContent = new
{
state = newState
};
var content = new StringContent(JsonSerializer.Serialize(jsonContent), Encoding.UTF8, "application/json");
try
{
// Sende die POST-Anfrage
HttpResponseMessage response = await client.PostAsync(url, content);
// Überprüfe den Statuscode der Antwort
if (response.IsSuccessStatusCode)
{
Console.WriteLine("Status erfolgreich geändert.");
}
else
{
Console.WriteLine($"Fehler: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Ein Fehler ist aufgetreten: {ex.Message}");
}
}
private void FaderControl_PointerWheelChanged(object? sender, Avalonia.Input.PointerWheelEventArgs e)
{
if (FaderControl.IsVisible && FaderControl.IsEnabled)
{
double delta = e.Delta.Y > 0 ? 0.5 : -0.5;
double newValue = FaderControl.Value + delta;
if (newValue < FaderControl.Minimum) newValue = FaderControl.Minimum;
if (newValue > FaderControl.Maximum) newValue = FaderControl.Maximum;
FaderControl.Value = newValue;
}
}
} }

12
Settings.cs Normal file
View file

@ -0,0 +1,12 @@
namespace HAcontrol;
public class Settings
{
public string HomeAssistantUrl { get; set; } = string.Empty;
public string AccessToken { get; set; } = string.Empty;
public string WebhookId { get; set; } = string.Empty;
public string Entity { get; set; } = string.Empty;
public string FaderEntity { get; set; } = string.Empty;
public double WindowTop { get; set; } = 0;
public double WindowLeft { get; set; } = 0;
}

View file

@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="HAcontrol.SettingsWindow" x:Class="HAcontrol.SettingsWindow"
Title="Einstellungen" Width="400" Height="340"> Title="Einstellungen" Width="400" Height="400">
<StackPanel Margin="20" Spacing="10"> <StackPanel Margin="20" Spacing="10">
<TextBlock Text="Home Assistant URL:"/> <TextBlock Text="Home Assistant URL:"/>
<TextBox x:Name="UrlBox"/> <TextBox x:Name="UrlBox"/>
@ -11,8 +11,10 @@
<TextBox x:Name="TokenBox"/> <TextBox x:Name="TokenBox"/>
<TextBlock Text="Webhook ID:"/> <TextBlock Text="Webhook ID:"/>
<TextBox x:Name="WebhookBox"/> <TextBox x:Name="WebhookBox"/>
<TextBlock Text="HA Entity:"/> <TextBlock Text="HA entity:"/>
<TextBox x:Name="EntityBox"/> <TextBox x:Name="EntityBox"/>
<TextBlock Text="Fader entity:"/>
<TextBox x:Name="FaderEntityBox"/>
<Button Content="Speichern" HorizontalAlignment="Right" Click="Save_Click"/> <Button Content="Speichern" HorizontalAlignment="Right" Click="Save_Click"/>
</StackPanel> </StackPanel>
</Window> </Window>

View file

@ -19,17 +19,18 @@ public partial class SettingsWindow : Window
private void LoadSettings() private void LoadSettings()
{ {
if (File.Exists(SettingsFile)) if (!File.Exists(SettingsFile))
return;
var json = File.ReadAllText(SettingsFile);
var settings = JsonSerializer.Deserialize<Settings>(json);
if (settings != null)
{ {
var json = File.ReadAllText(SettingsFile); UrlBox.Text = settings.HomeAssistantUrl;
var settings = JsonSerializer.Deserialize<Settings>(json); TokenBox.Text = settings.AccessToken;
if (settings != null) WebhookBox.Text = settings.WebhookId;
{ EntityBox.Text = settings.Entity;
UrlBox.Text = settings.HomeAssistantUrl; FaderEntityBox.Text = settings.FaderEntity;
TokenBox.Text = settings.AccessToken;
WebhookBox.Text = settings.WebhookId;
EntityBox.Text = settings.Entity;
}
} }
} }
@ -40,20 +41,14 @@ public partial class SettingsWindow : Window
HomeAssistantUrl = UrlBox.Text ?? string.Empty, HomeAssistantUrl = UrlBox.Text ?? string.Empty,
AccessToken = TokenBox.Text ?? string.Empty, AccessToken = TokenBox.Text ?? string.Empty,
WebhookId = WebhookBox.Text ?? string.Empty, WebhookId = WebhookBox.Text ?? string.Empty,
Entity = EntityBox.Text ?? string.Empty Entity = EntityBox.Text ?? string.Empty,
FaderEntity = FaderEntityBox.Text ?? string.Empty
}; };
var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true }); var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(SettingsFile, json); File.WriteAllText(SettingsFile, json);
this.Close(); this.Close();
MainWindow.Self.LoadSettings(); MainWindow.Self?.LoadSettings();
} }
} }
public class Settings
{
public string HomeAssistantUrl { get; set; } = string.Empty;
public string AccessToken { get; set; } = string.Empty;
public string WebhookId { get; set; } = string.Empty;
public string Entity { get; set; } = string.Empty;
}